text: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.
   **/
  
  #include <algorithm>
  #include <cstddef>
  #include <cstdint>
  #include <cstring>
  #include <limits>
  #include <memory>
  #include <string>
  #include <utility>
  #include <vector>
  
  #include "types/DatetimeLit.hpp"
  #include "types/IntervalLit.hpp"
  #include "types/Type.hpp"
  #include "types/TypeFactory.hpp"
  #include "types/TypeID.hpp"
  #include "types/TypedValue.hpp"
  #include "types/operations/Operation.pb.h"
  #include "types/operations/comparisons/Comparison.hpp"
  #include "types/operations/comparisons/ComparisonFactory.hpp"
  #include "types/operations/comparisons/ComparisonID.hpp"
  #include "utility/Macros.hpp"
  #include "utility/ScopedBuffer.hpp"
  
  #include "gtest/gtest.h"
  
  using std::int64_t;
  using std::min;
  using std::numeric_limits;
  using std::pair;
  using std::string;
  using std::strlen;
  using std::unique_ptr;
  using std::vector;
  
  namespace quickstep {
  
  namespace {
  
  static const char kSampleStringShort[] = "foo";
  
  static const char kSampleStringLong[] =
      "Space is big. You just won't believe how vastly, hugely, mind-bogglingly "
      "big it is. I mean, you may think it's a long way down the road to the "
      "chemist's, but that's just peanuts to space.";
  
  }  // namespace
  
  class ComparisonTest : public ::testing::Test {
   protected:
    typedef vector<const TypedValue *>::const_iterator const_iterator;
  
    virtual void SetUp() {
      int_null_.reset(new TypedValue(kInt));
      int_zero_.reset(new TypedValue(0));
      int_positive_.reset(new TypedValue(12345));
      int_negative_.reset(new TypedValue(- int_positive_->getLiteral<int>()));
      int_max_.reset(new TypedValue(numeric_limits<int>::max()));
      int_min_.reset(new TypedValue(numeric_limits<int>::min()));
  
      numeric_typed_values_.emplace_back(int_null_.get(),
                                         &TypeFactory::GetType(kInt, true));
      numeric_typed_values_.emplace_back(int_zero_.get(),
                                         &TypeFactory::GetType(kInt, false));
      numeric_typed_values_.emplace_back(int_positive_.get(),
                                         &TypeFactory::GetType(kInt, false));
      numeric_typed_values_.emplace_back(int_negative_.get(),
                                         &TypeFactory::GetType(kInt, false));
      numeric_typed_values_.emplace_back(int_max_.get(),
                                         &TypeFactory::GetType(kInt, false));
      numeric_typed_values_.emplace_back(int_min_.get(),
                                         &TypeFactory::GetType(kInt, false));
  
      long_null_.reset(new TypedValue(kLong));
      long_zero_.reset(new TypedValue(static_cast<int64_t>(0)));
      long_positive_.reset(new TypedValue(static_cast<int64_t>(12345)));
      long_negative_.reset(new TypedValue(- long_positive_->getLiteral<int64_t>()));
      long_max_.reset(new TypedValue(numeric_limits<int64_t>::max()));
      long_min_.reset(new TypedValue(numeric_limits<int64_t>::min()));
  
      numeric_typed_values_.emplace_back(long_null_.get(),
                                         &TypeFactory::GetType(kLong, true));
      numeric_typed_values_.emplace_back(long_zero_.get(),
                                         &TypeFactory::GetType(kLong, false));
      numeric_typed_values_.emplace_back(long_positive_.get(),
                                         &TypeFactory::GetType(kLong, false));
      numeric_typed_values_.emplace_back(long_negative_.get(),
                                         &TypeFactory::GetType(kLong, false));
      numeric_typed_values_.emplace_back(long_max_.get(),
                                         &TypeFactory::GetType(kLong, false));
      numeric_typed_values_.emplace_back(long_min_.get(),
                                         &TypeFactory::GetType(kLong, false));
  
      float_null_.reset(new TypedValue(kFloat));
      float_zero_.reset(new TypedValue(static_cast<float>(0.0)));
      float_positive_.reset(new TypedValue(static_cast<float>(123.45)));
      float_negative_.reset(new TypedValue(- float_positive_->getLiteral<float>()));
      float_max_.reset(new TypedValue(numeric_limits<float>::max()));
      float_min_.reset(new TypedValue(numeric_limits<float>::min()));
  
      numeric_typed_values_.emplace_back(float_null_.get(),
                                         &TypeFactory::GetType(kFloat, true));
      numeric_typed_values_.emplace_back(float_zero_.get(),
                                         &TypeFactory::GetType(kFloat, false));
      numeric_typed_values_.emplace_back(float_positive_.get(),
                                         &TypeFactory::GetType(kFloat, false));
      numeric_typed_values_.emplace_back(float_negative_.get(),
                                         &TypeFactory::GetType(kFloat, false));
      numeric_typed_values_.emplace_back(float_max_.get(),
                                         &TypeFactory::GetType(kFloat, false));
      numeric_typed_values_.emplace_back(float_min_.get(),
                                         &TypeFactory::GetType(kFloat, false));
  
      double_null_.reset(new TypedValue(kDouble));
      double_zero_.reset(new TypedValue(static_cast<double>(0)));
      double_positive_.reset(new TypedValue(static_cast<double>(123.45)));
      double_negative_.reset(new TypedValue(- double_positive_->getLiteral<double>()));
      double_max_.reset(new TypedValue(numeric_limits<double>::max()));
      double_min_.reset(new TypedValue(numeric_limits<double>::min()));
  
      numeric_typed_values_.emplace_back(double_null_.get(),
                                         &TypeFactory::GetType(kDouble, true));
      numeric_typed_values_.emplace_back(double_zero_.get(),
                                         &TypeFactory::GetType(kDouble, false));
      numeric_typed_values_.emplace_back(double_positive_.get(),
                                         &TypeFactory::GetType(kDouble, false));
      numeric_typed_values_.emplace_back(double_negative_.get(),
                                         &TypeFactory::GetType(kDouble, false));
      numeric_typed_values_.emplace_back(double_max_.get(),
                                         &TypeFactory::GetType(kDouble, false));
      numeric_typed_values_.emplace_back(double_min_.get(),
                                         &TypeFactory::GetType(kDouble, false));
  
      date_null_.reset(new TypedValue(kDate));
      date_positive_.reset(new TypedValue(DateLit::Create(2016, 7, 15)));
      date_negative_.reset(new TypedValue(DateLit::Create(-18017, 4, 13)));
      date_max_.reset(new TypedValue(DateLit::Create(99999, 12, 31)));
      date_min_.reset(new TypedValue(DateLit::Create(-99999, 1, 1)));
  
      date_typed_values_.emplace_back(date_null_.get(),
                                      &TypeFactory::GetType(kDate, true));
      date_typed_values_.emplace_back(date_positive_.get(),
                                      &TypeFactory::GetType(kDate, false));
      date_typed_values_.emplace_back(date_negative_.get(),
                                      &TypeFactory::GetType(kDate, false));
      date_typed_values_.emplace_back(date_max_.get(),
                                      &TypeFactory::GetType(kDate, false));
      date_typed_values_.emplace_back(date_min_.get(),
                                      &TypeFactory::GetType(kDate, false));
  
      datetime_null_.reset(new TypedValue(kDatetime));
      DatetimeLit datetime;
      datetime.ticks = 0;
      datetime_zero_.reset(new TypedValue(datetime));
      datetime.ticks = 12345678;
      datetime_positive_.reset(new TypedValue(datetime));
      datetime.ticks = -87654321;
      datetime_negative_.reset(new TypedValue(datetime));
      datetime.ticks = numeric_limits<int64_t>::max();
      datetime_max_.reset(new TypedValue(datetime));
      datetime.ticks = numeric_limits<int64_t>::min();
      datetime_min_.reset(new TypedValue(datetime));
  
      datetime_typed_values_.emplace_back(datetime_null_.get(),
                                          &TypeFactory::GetType(kDatetime, true));
      datetime_typed_values_.emplace_back(datetime_zero_.get(),
                                          &TypeFactory::GetType(kDatetime, false));
      datetime_typed_values_.emplace_back(datetime_positive_.get(),
                                          &TypeFactory::GetType(kDatetime, false));
      datetime_typed_values_.emplace_back(datetime_negative_.get(),
                                          &TypeFactory::GetType(kDatetime, false));
      datetime_typed_values_.emplace_back(datetime_max_.get(),
                                          &TypeFactory::GetType(kDatetime, false));
      datetime_typed_values_.emplace_back(datetime_min_.get(),
                                          &TypeFactory::GetType(kDatetime, false));
  
      datetime_interval_null_.reset(new TypedValue(kDatetimeInterval));
      DatetimeIntervalLit datetime_interval;
      datetime_interval.interval_ticks = 0;
      datetime_interval_zero_.reset(new TypedValue(datetime_interval));
      datetime_interval.interval_ticks = 87654321;
      datetime_interval_positive_.reset(new TypedValue(datetime_interval));
      datetime_interval.interval_ticks = -12345678;
      datetime_interval_negative_.reset(new TypedValue(datetime_interval));
  
      datetime_interval_typed_values_.emplace_back(datetime_interval_null_.get(),
                                                   &TypeFactory::GetType(kDatetimeInterval, true));
      datetime_interval_typed_values_.emplace_back(datetime_interval_zero_.get(),
                                                   &TypeFactory::GetType(kDatetimeInterval, false));
      datetime_interval_typed_values_.emplace_back(datetime_interval_positive_.get(),
                                                   &TypeFactory::GetType(kDatetimeInterval, false));
      datetime_interval_typed_values_.emplace_back(datetime_interval_negative_.get(),
                                                   &TypeFactory::GetType(kDatetimeInterval, false));
  
      year_month_interval_null_.reset(new TypedValue(kYearMonthInterval));
      YearMonthIntervalLit year_month_interval;
      year_month_interval.months = 0;
      year_month_interval_zero_.reset(new TypedValue(year_month_interval));
      year_month_interval.months = 12;  // 1 year forward.
      year_month_interval_positive_.reset(new TypedValue(year_month_interval));
      year_month_interval.months = -3;  // 3 months backward.
      year_month_interval_negative_.reset(new TypedValue(year_month_interval));
  
      year_month_interval_typed_values_.emplace_back(year_month_interval_null_.get(),
                                                     &TypeFactory::GetType(kYearMonthInterval, true));
      year_month_interval_typed_values_.emplace_back(year_month_interval_zero_.get(),
                                                     &TypeFactory::GetType(kYearMonthInterval, false));
      year_month_interval_typed_values_.emplace_back(year_month_interval_positive_.get(),
                                                     &TypeFactory::GetType(kYearMonthInterval, false));
      year_month_interval_typed_values_.emplace_back(year_month_interval_negative_.get(),
                                                     &TypeFactory::GetType(kYearMonthInterval, false));
  
      char_null_.reset(new TypedValue(kChar));
      varchar_null_.reset(new TypedValue(kVarChar));
  
      string_typed_values_.emplace_back(char_null_.get(),
                                        &TypeFactory::GetType(kChar, 10, true));
      string_typed_values_.emplace_back(varchar_null_.get(),
                                        &TypeFactory::GetType(kVarChar, 10, true));
  
      const std::size_t string_short_length = strlen(kSampleStringShort);
      char_ref_short_.reset(new TypedValue(kChar, kSampleStringShort, string_short_length));
      char_lit_short_.reset(new TypedValue(kChar, kSampleStringShort, string_short_length));
      char_lit_short_->ensureNotReference();
  
      char_ref_null_terminated_short_.reset(new TypedValue(kChar, kSampleStringShort, string_short_length + 1));
      char_lit_null_terminated_short_.reset(new TypedValue(kChar, kSampleStringShort, string_short_length + 1));
      char_lit_null_terminated_short_->ensureNotReference();
  
      extended_short_char_buffer_.reset(new ScopedBuffer(strlen(kSampleStringShort) + 5));
      std::memset(extended_short_char_buffer_->get(), 0, string_short_length + 5);
      std::memcpy(extended_short_char_buffer_->get(), kSampleStringShort, string_short_length);
  
      char_ref_extended_short_.reset(new TypedValue(kChar, extended_short_char_buffer_->get(), string_short_length + 5));
  
      char_lit_extended_short_.reset(new TypedValue(kChar, extended_short_char_buffer_->get(), string_short_length + 5));
      char_lit_extended_short_->ensureNotReference();
  
      varchar_ref_short_.reset(new TypedValue(kVarChar, kSampleStringShort, string_short_length + 1));
      varchar_lit_short_.reset(new TypedValue(kVarChar, kSampleStringShort, string_short_length + 1));
      varchar_lit_short_->ensureNotReference();
  
      string_typed_values_.emplace_back(
          char_ref_short_.get(),
          &TypeFactory::GetType(kChar,
                                char_ref_short_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_lit_short_.get(),
          &TypeFactory::GetType(kChar,
                                char_lit_short_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_ref_null_terminated_short_.get(),
          &TypeFactory::GetType(kChar,
                                char_ref_null_terminated_short_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_lit_null_terminated_short_.get(),
          &TypeFactory::GetType(kChar,
                                char_lit_null_terminated_short_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_ref_extended_short_.get(),
          &TypeFactory::GetType(kChar,
                                char_ref_extended_short_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_lit_extended_short_.get(),
          &TypeFactory::GetType(kChar,
                                char_lit_extended_short_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          varchar_ref_short_.get(),
          &TypeFactory::GetType(kVarChar,
                                varchar_ref_short_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          varchar_lit_short_.get(),
          &TypeFactory::GetType(kVarChar,
                                varchar_lit_short_->getAsciiStringLength(),
                                false));
  
      const std::size_t string_long_length = strlen(kSampleStringLong);
      char_ref_long_.reset(new TypedValue(kChar, kSampleStringLong, string_long_length));
      char_lit_long_.reset(new TypedValue(kChar, kSampleStringLong, string_long_length));
      char_lit_long_->ensureNotReference();
  
      char_ref_null_terminated_long_.reset(new TypedValue(kChar, kSampleStringLong, string_long_length + 1));
      char_lit_null_terminated_long_.reset(new TypedValue(kChar, kSampleStringLong, string_long_length + 1));
      char_lit_null_terminated_long_->ensureNotReference();
  
      extended_long_char_buffer_.reset(new ScopedBuffer(strlen(kSampleStringLong) + 5));
      std::memset(extended_long_char_buffer_->get(), 0, string_long_length + 5);
      std::memcpy(extended_long_char_buffer_->get(), kSampleStringLong, string_long_length);
  
      char_ref_extended_long_.reset(new TypedValue(kChar, extended_long_char_buffer_->get(), string_long_length + 5));
  
      char_lit_extended_long_.reset(new TypedValue(kChar, extended_long_char_buffer_->get(), string_long_length + 5));
      char_lit_extended_long_->ensureNotReference();
  
      varchar_ref_long_.reset(new TypedValue(kVarChar, kSampleStringLong, string_long_length + 1));
      varchar_lit_long_.reset(new TypedValue(kVarChar, kSampleStringLong, string_long_length + 1));
      varchar_lit_long_->ensureNotReference();
  
      string_typed_values_.emplace_back(
          char_ref_long_.get(),
          &TypeFactory::GetType(kChar,
                                char_ref_long_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_lit_long_.get(),
          &TypeFactory::GetType(kChar,
                                char_lit_long_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_ref_null_terminated_long_.get(),
          &TypeFactory::GetType(kChar,
                                char_ref_null_terminated_long_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_lit_null_terminated_long_.get(),
          &TypeFactory::GetType(kChar,
                                char_lit_null_terminated_long_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_ref_extended_long_.get(),
          &TypeFactory::GetType(kChar,
                                char_ref_extended_long_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          char_lit_extended_long_.get(),
          &TypeFactory::GetType(kChar,
                                char_lit_extended_long_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          varchar_ref_long_.get(),
          &TypeFactory::GetType(kVarChar,
                                varchar_ref_long_->getAsciiStringLength(),
                                false));
      string_typed_values_.emplace_back(
          varchar_lit_long_.get(),
          &TypeFactory::GetType(kVarChar,
                                varchar_lit_long_->getAsciiStringLength(),
                                false));
    }
  
    void checkComparison(const Comparison &comparison) {
      checkNumericComparison(comparison);
      checkStringComparison(comparison);
      checkDateComparison(comparison);
      checkDatetimeComparison(comparison);
      checkDatetimeIntervalComparison(comparison);
      checkYearMonthIntervalComparison(comparison);
    }
  
   private:
    template <typename NumericType>
    static NumericType GetTypedValueAsNumeric(const TypedValue &value) {
      switch (value.getTypeID()) {
        case kInt:
          return value.getLiteral<int>();
        case kLong:
          return value.getLiteral<std::int64_t>();
        case kFloat:
          return value.getLiteral<float>();
        case kDouble:
          return value.getLiteral<double>();
        default:
          FATAL_ERROR("TypedValue does not appear to be numeric.");
      }
    }
  
    void checkNumericComparison(const Comparison &comparison) {
      for (const std::pair<const TypedValue*, const Type*> &left_item : numeric_typed_values_) {
        for (const std::pair<const TypedValue*, const Type*> &right_item : numeric_typed_values_) {
          checkNumericComparisonChecked(comparison,
                                        *left_item.first, *left_item.second,
                                        *right_item.first, *right_item.second);
          checkNumericComparisonUnchecked(comparison,
                                          *left_item.first, *left_item.second,
                                          *right_item.first, *right_item.second);
        }
      }
    }
  
    void checkNumericComparisonChecked(const Comparison &comparison,
                                       const TypedValue &left,
                                       const Type &left_type,
                                       const TypedValue &right,
                                       const Type &right_type) {
      if (left.isNull() || right.isNull()) {
        EXPECT_FALSE(comparison.compareTypedValuesChecked(left, left_type,
                                                          right, right_type));
        return;
      }
  
      const Type *expected_type = TypeFactory::GetUnifyingType(left_type,
                                                               right_type);
      ASSERT_NE(expected_type, nullptr);
  
      switch (expected_type->getTypeID()) {
        case kInt:
          checkNumericComparisonCheckedHelper<int>(comparison,
                                                   left, left_type,
                                                   right, right_type);
          break;
        case kLong:
          checkNumericComparisonCheckedHelper<int64_t>(comparison,
                                                       left, left_type,
                                                       right, right_type);
          break;
        case kFloat:
          checkNumericComparisonCheckedHelper<float>(comparison,
                                                     left, left_type,
                                                     right, right_type);
          break;
        case kDouble:
          checkNumericComparisonCheckedHelper<double>(comparison,
                                                      left, left_type,
                                                      right, right_type);
          break;
        default:
          FATAL_ERROR("Unsupported result type for the comparison.");
      }
    }
  
    template <typename NumericType>
    inline void checkNumericComparisonCheckedHelper(const Comparison &comparison,
                                                    const TypedValue &left,
                                                    const Type &left_type,
                                                    const TypedValue &right,
                                                    const Type &right_type) {
      if (left.isNull() || right.isNull()) {
        EXPECT_FALSE(comparison.compareTypedValuesChecked(left, left_type,
                                                          right, right_type));
        return;
      }
  
      bool expected = checkNumericComparisonHelper<NumericType>(comparison, left, right);
      EXPECT_EQ(expected, comparison.compareTypedValuesChecked(left, left_type,
                                                               right, right_type));
    }
  
    void checkNumericComparisonUnchecked(const Comparison &comparison,
                                         const TypedValue &left,
                                         const Type &left_type,
                                         const TypedValue &right,
                                         const Type &right_type) {
      unique_ptr<UncheckedComparator> comparator(
          comparison.makeUncheckedComparatorForTypes(left_type,
                                                     right_type));
  
      if (left.isNull() || right.isNull()) {
        EXPECT_FALSE(comparator->compareTypedValues(left, right));
        EXPECT_FALSE(comparator->compareDataPtrs(
            left.isNull() ? nullptr : left.getDataPtr(),
            right.isNull() ? nullptr : right.getDataPtr()));
        return;
      }
  
      const Type *expected_type = TypeFactory::GetUnifyingType(left_type,
                                                               right_type);
      ASSERT_NE(expected_type, nullptr);
  
      switch (expected_type->getTypeID()) {
        case kInt:
          checkNumericComparisonUncheckedHelper<int>(comparison, *comparator,
                                                     left, left_type,
                                                     right, right_type);
          break;
        case kLong:
          checkNumericComparisonUncheckedHelper<int64_t>(comparison, *comparator,
                                                         left, left_type,
                                                         right, right_type);
          break;
        case kFloat:
          checkNumericComparisonUncheckedHelper<float>(comparison, *comparator,
                                                       left, left_type,
                                                       right, right_type);
          break;
        case kDouble:
          checkNumericComparisonUncheckedHelper<double>(comparison, *comparator,
                                                        left, left_type,
                                                        right, right_type);
          break;
        default:
          FATAL_ERROR("Unsupported result types for the comparison.");
      }
    }
  
    template <typename NumericType>
    inline void checkNumericComparisonUncheckedHelper(const Comparison &comparison,
                                                      const UncheckedComparator &comparator,
                                                      const TypedValue &left,
                                                      const Type &left_type,
                                                      const TypedValue &right,
                                                      const Type &right_type) {
      bool expected = checkNumericComparisonHelper<NumericType>(comparison, left, right);
      EXPECT_EQ(expected, comparator.compareTypedValues(left, right));
      EXPECT_EQ(expected, comparator.compareDataPtrs(left.getDataPtr(), right.getDataPtr()));
    }
  
    template <typename NumericType>
    inline bool checkNumericComparisonHelper(const Comparison &comparison,
                                             const TypedValue &left,
                                             const TypedValue &right) {
      const NumericType left_num = GetTypedValueAsNumeric<NumericType>(left);
      const NumericType right_num = GetTypedValueAsNumeric<NumericType>(right);
  
      switch (comparison.getComparisonID()) {
        case ComparisonID::kEqual:
          return left_num == right_num;
        case ComparisonID::kNotEqual:
          return left_num != right_num;
        case ComparisonID::kLess:
          return left_num < right_num;
        case ComparisonID::kLessOrEqual:
          return left_num <= right_num;
        case ComparisonID::kGreater:
          return left_num > right_num;
        case ComparisonID::kGreaterOrEqual:
          return left_num >= right_num;
        default:
          FATAL_ERROR("Unsupported comparison type.");
      }
    }
  
    void checkDateComparison(const Comparison &comparison) {
      for (const std::pair<const TypedValue*, const Type*> &left_item : date_typed_values_) {
        for (const std::pair<const TypedValue*, const Type*> &right_item : date_typed_values_) {
          checkDateComparisonChecked<DateLit>(comparison,
                                              *left_item.first, *left_item.second,
                                              *right_item.first, *right_item.second);
          checkDateComparisonUnchecked<DateLit>(comparison,
                                                *left_item.first, *left_item.second,
                                                *right_item.first, *right_item.second);
        }
      }
    }
  
    void checkDatetimeComparison(const Comparison &comparison) {
      for (const std::pair<const TypedValue*, const Type*> &left_item : datetime_typed_values_) {
        for (const std::pair<const TypedValue*, const Type*> &right_item : datetime_typed_values_) {
          checkDateComparisonChecked<DatetimeLit>(comparison,
                                                  *left_item.first, *left_item.second,
                                                  *right_item.first, *right_item.second);
          checkDateComparisonUnchecked<DatetimeLit>(comparison,
                                                    *left_item.first, *left_item.second,
                                                    *right_item.first, *right_item.second);
        }
      }
    }
  
    void checkDatetimeIntervalComparison(const Comparison &comparison) {
      for (const std::pair<const TypedValue*, const Type*> &left_item
           : datetime_interval_typed_values_) {
        for (const std::pair<const TypedValue*, const Type*> &right_item
             : datetime_interval_typed_values_) {
          checkDateComparisonChecked<DatetimeIntervalLit>(comparison,
                                                          *left_item.first, *left_item.second,
                                                          *right_item.first, *right_item.second);
          checkDateComparisonUnchecked<DatetimeIntervalLit>(comparison,
                                                            *left_item.first, *left_item.second,
                                                            *right_item.first, *right_item.second);
        }
      }
    }
  
    void checkYearMonthIntervalComparison(const Comparison &comparison) {
      for (const std::pair<const TypedValue*, const Type*> &left_item
           : year_month_interval_typed_values_) {
        for (const std::pair<const TypedValue*, const Type*> &right_item
             : year_month_interval_typed_values_) {
          checkDateComparisonChecked<YearMonthIntervalLit>(comparison,
                                                           *left_item.first, *left_item.second,
                                                           *right_item.first, *right_item.second);
          checkDateComparisonUnchecked<YearMonthIntervalLit>(comparison,
                                                             *left_item.first, *left_item.second,
                                                             *right_item.first, *right_item.second);
        }
      }
    }
  
    template <typename DateType>
    void checkDateComparisonChecked(const Comparison &comparison,
                                    const TypedValue &left,
                                    const Type &left_type,
                                    const TypedValue &right,
                                    const Type &right_type) {
      if (left.isNull() || right.isNull()) {
        EXPECT_FALSE(comparison.compareTypedValuesChecked(left, left_type,
                                                          right, right_type));
        return;
      }
  
      bool expected = checkDateComparisonHelper<DateType>(comparison, left, right);
      EXPECT_EQ(expected, comparison.compareTypedValuesChecked(left, left_type,
                                                               right, right_type));
    }
  
    template <typename DateType>
    void checkDateComparisonUnchecked(const Comparison &comparison,
                                      const TypedValue &left,
                                      const Type &left_type,
                                      const TypedValue &right,
                                      const Type &right_type) {
      unique_ptr<UncheckedComparator> comparator(
          comparison.makeUncheckedComparatorForTypes(left_type,
                                                     right_type));
  
      if (left.isNull() || right.isNull()) {
        EXPECT_FALSE(comparator->compareTypedValues(left, right));
        EXPECT_FALSE(comparator->compareDataPtrs(
            left.isNull() ? nullptr : left.getDataPtr(),
            right.isNull() ? nullptr : right.getDataPtr()));
        return;
      }
  
      bool expected = checkDateComparisonHelper<DateType>(comparison, left, right);
      EXPECT_EQ(expected, comparator->compareTypedValues(left, right));
      EXPECT_EQ(expected, comparator->compareDataPtrs(left.getDataPtr(), right.getDataPtr()));
    }
  
    template <typename DateType>
    inline bool checkDateComparisonHelper(const Comparison &comparison,
                                          const TypedValue &left,
                                          const TypedValue &right) {
      switch (comparison.getComparisonID()) {
        case ComparisonID::kEqual:
          return left.getLiteral<DateType>() == right.getLiteral<DateType>();
        case ComparisonID::kNotEqual:
          return left.getLiteral<DateType>() != right.getLiteral<DateType>();
        case ComparisonID::kLess:
          return left.getLiteral<DateType>() < right.getLiteral<DateType>();
        case ComparisonID::kLessOrEqual:
          return left.getLiteral<DateType>() <= right.getLiteral<DateType>();
        case ComparisonID::kGreater:
          return left.getLiteral<DateType>() > right.getLiteral<DateType>();
        case ComparisonID::kGreaterOrEqual:
          return left.getLiteral<DateType>() >= right.getLiteral<DateType>();
        default:
          FATAL_ERROR("Unsupported comparison type.");
      }
    }
  
    void checkStringComparison(const Comparison &comparison) {
      for (const std::pair<const TypedValue*, const Type*> &left_item : string_typed_values_) {
        for (const std::pair<const TypedValue*, const Type*> &right_item : string_typed_values_) {
          checkStringComparisonChecked(comparison,
                                       *left_item.first, *left_item.second,
                                       *right_item.first, *right_item.second);
          checkStringComparisonUnchecked(comparison,
                                         *left_item.first, *left_item.second,
                                         *right_item.first, *right_item.second);
        }
      }
    }
  
    void checkStringComparisonChecked(const Comparison &comparison,
                                      const TypedValue &left,
                                      const Type &left_type,
                                      const TypedValue &right,
                                      const Type &right_type) {
      if (left.isNull() || right.isNull()) {
        EXPECT_FALSE(comparison.compareTypedValuesChecked(left, left_type,
                                                          right, right_type));
        return;
      }
  
      bool expected = checkStringComparisonHelper(comparison, left, right);
      EXPECT_EQ(expected, comparison.compareTypedValuesChecked(left, left_type,
                                                               right, right_type));
    }
  
    void checkStringComparisonUnchecked(const Comparison &comparison,
                                        const TypedValue &left,
                                        const Type &left_type,
                                        const TypedValue &right,
                                        const Type &right_type) {
      unique_ptr<UncheckedComparator> comparator(
          comparison.makeUncheckedComparatorForTypes(left_type,
                                                     right_type));
  
      if (left.isNull() || right.isNull()) {
        EXPECT_FALSE(comparator->compareTypedValues(left, right));
        EXPECT_FALSE(comparator->compareDataPtrs(
            left.isNull() ? nullptr : left.getDataPtr(),
            right.isNull() ? nullptr : right.getDataPtr()));
        return;
      }
  
      bool expected = checkStringComparisonHelper(comparison, left, right);
      EXPECT_EQ(expected, comparator->compareTypedValues(left, right));
      EXPECT_EQ(expected, comparator->compareDataPtrs(left.getDataPtr(), right.getDataPtr()));
    }
  
    inline bool checkStringComparisonHelper(const Comparison &comparison,
                                            const TypedValue &left,
                                            const TypedValue &right) {
      const std::size_t left_length = left.getAsciiStringLength();
      const std::size_t right_length = right.getAsciiStringLength();
      const int compare_result =
          strncmp(static_cast<const char*>(left.getOutOfLineData()),
                  static_cast<const char*>(right.getOutOfLineData()),
                  min(left_length, right_length));
  
      switch (comparison.getComparisonID()) {
        case ComparisonID::kEqual:
          return compare_result == 0 && left_length == right_length;
        case ComparisonID::kNotEqual:
          return !(compare_result == 0 && left_length == right_length);
        case ComparisonID::kLess:
          return compare_result < 0 || (compare_result == 0 && left_length < right_length);
        case ComparisonID::kLessOrEqual:
          return compare_result < 0 || (compare_result == 0 && left_length <= right_length);
        case ComparisonID::kGreater:
          return compare_result > 0 || (compare_result == 0 && left_length > right_length);
        case ComparisonID::kGreaterOrEqual:
          return compare_result > 0 || (compare_result == 0 && left_length >= right_length);
        default:
          FATAL_ERROR("Unsupported comparison type.");
      }
    }
  
    unique_ptr<TypedValue>
        int_null_, int_zero_, int_positive_, int_negative_, int_max_, int_min_,
        long_null_, long_zero_, long_positive_, long_negative_, long_max_, long_min_,
        float_null_, float_zero_, float_positive_, float_negative_, float_max_, float_min_,
        double_null_, double_zero_, double_positive_, double_negative_, double_max_, double_min_,
        char_null_, char_ref_short_, char_lit_short_, char_ref_long_, char_lit_long_,
        char_ref_null_terminated_short_, char_lit_null_terminated_short_,
        char_ref_null_terminated_long_, char_lit_null_terminated_long_,
        char_ref_extended_short_, char_lit_extended_short_,
        char_ref_extended_long_, char_lit_extended_long_,
        varchar_null_, varchar_ref_short_, varchar_lit_short_, varchar_ref_long_, varchar_lit_long_,
        date_null_, date_positive_, date_negative_, date_max_, date_min_,
        datetime_null_, datetime_zero_, datetime_positive_, datetime_negative_, datetime_max_, datetime_min_,
        datetime_interval_null_, datetime_interval_zero_, datetime_interval_positive_, datetime_interval_negative_,
        year_month_interval_null_, year_month_interval_zero_,
        year_month_interval_positive_, year_month_interval_negative_;
  
    unique_ptr<ScopedBuffer> extended_short_char_buffer_, extended_long_char_buffer_;
  
    vector<pair<const TypedValue*, const Type*>>
        numeric_typed_values_, date_typed_values_, datetime_typed_values_, datetime_interval_typed_values_,
        year_month_interval_typed_values_, string_typed_values_;
  };
  
  TEST_F(ComparisonTest, ComparisonTests) {
    checkComparison(ComparisonFactory::GetComparison(ComparisonID::kEqual));
    checkComparison(ComparisonFactory::GetComparison(ComparisonID::kNotEqual));
    checkComparison(ComparisonFactory::GetComparison(ComparisonID::kLess));
    checkComparison(ComparisonFactory::GetComparison(ComparisonID::kLessOrEqual));
    checkComparison(ComparisonFactory::GetComparison(ComparisonID::kGreater));
    checkComparison(ComparisonFactory::GetComparison(ComparisonID::kGreaterOrEqual));
  }
  
  void CheckComparisonSerialization(const Comparison &comparison) {
    serialization::Comparison proto = comparison.getProto();
    switch (comparison.getComparisonID()) {
      case ComparisonID::kEqual:
        EXPECT_EQ(serialization::Comparison::EQUAL, proto.comparison_id());
        break;
      case ComparisonID::kNotEqual:
        EXPECT_EQ(serialization::Comparison::NOT_EQUAL, proto.comparison_id());
        break;
      case ComparisonID::kLess:
        EXPECT_EQ(serialization::Comparison::LESS, proto.comparison_id());
        break;
      case ComparisonID::kLessOrEqual:
        EXPECT_EQ(serialization::Comparison::LESS_OR_EQUAL, proto.comparison_id());
        break;
      case ComparisonID::kGreater:
        EXPECT_EQ(serialization::Comparison::GREATER, proto.comparison_id());
        break;
      case ComparisonID::kGreaterOrEqual:
        EXPECT_EQ(serialization::Comparison::GREATER_OR_EQUAL, proto.comparison_id());
        break;
      case ComparisonID::kLike:
        EXPECT_EQ(serialization::Comparison::LIKE, proto.comparison_id());
        break;
      case ComparisonID::kNotLike:
        EXPECT_EQ(serialization::Comparison::NOT_LIKE, proto.comparison_id());
        break;
      case ComparisonID::kRegexMatch:
        EXPECT_EQ(serialization::Comparison::REGEX_MATCH, proto.comparison_id());
        break;
      case ComparisonID::kNotRegexMatch:
        EXPECT_EQ(serialization::Comparison::NOT_REGEX_MATCH, proto.comparison_id());
        break;
  
      default:
        FATAL_ERROR("operation is an unknown Comparison in CheckComparisonSerialization");
    }
  
    EXPECT_TRUE(comparison.equals(ComparisonFactory::ReconstructFromProto(proto)));
  }
  
  TEST_F(ComparisonTest, ComparisonProtoSerializationTest) {
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kEqual));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kNotEqual));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kLess));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kLessOrEqual));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kGreater));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kGreaterOrEqual));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kLike));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kNotLike));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kRegexMatch));
    CheckComparisonSerialization(ComparisonFactory::GetComparison(ComparisonID::kNotRegexMatch));
  }
  
  // The 6 basic comparisons all have the same applicability to types. This
  // general-purpose function checks Comparison::canComparePartialTypes() for any
  // of them.
  void CheckBasicComparisonApplicableToPartialTypes(const Comparison &comparison) {
    // Assume applicable when neither side of the comparison is known.
    EXPECT_TRUE(comparison.canComparePartialTypes(nullptr, nullptr));
  
    // If only one argument is known, any orderable Type is OK.
    for (const TypeID type_id
         : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
      EXPECT_TRUE(comparison.canComparePartialTypes(
          &TypeFactory::GetType(type_id, false), nullptr));
      EXPECT_TRUE(comparison.canComparePartialTypes(
          &TypeFactory::GetType(type_id, true), nullptr));
      EXPECT_TRUE(comparison.canComparePartialTypes(
          nullptr, &TypeFactory::GetType(type_id, false)));
      EXPECT_TRUE(comparison.canComparePartialTypes(
          nullptr, &TypeFactory::GetType(type_id, true)));
    }
  
    for (const TypeID string_type_id : {kChar, kVarChar}) {
      EXPECT_TRUE(comparison.canComparePartialTypes(
          &TypeFactory::GetType(string_type_id, 10, false), nullptr));
      EXPECT_TRUE(comparison.canComparePartialTypes(
          &TypeFactory::GetType(string_type_id, 10, true), nullptr));
      EXPECT_TRUE(comparison.canComparePartialTypes(
          nullptr, &TypeFactory::GetType(string_type_id, 10, false)));
      EXPECT_TRUE(comparison.canComparePartialTypes(
          nullptr, &TypeFactory::GetType(string_type_id, 10, true)));
    }
  
    // If both types are known, expect the same thing as canCompareTypes().
    for (const TypeID first_type_id
         : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
      const Type &first_type = TypeFactory::GetType(first_type_id, false);
      const Type &first_type_nullable = TypeFactory::GetType(first_type_id, true);
  
      for (const TypeID second_type_id
           : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
        const Type &second_type = TypeFactory::GetType(second_type_id, false);
        const Type &second_type_nullable = TypeFactory::GetType(second_type_id, true);
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type),
                  comparison.canComparePartialTypes(&first_type, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type, &second_type_nullable));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type_nullable));
      }
  
      for (const TypeID second_type_id : {kChar, kVarChar}) {
        const Type &second_type = TypeFactory::GetType(second_type_id, 10, false);
        const Type &second_type_nullable = TypeFactory::GetType(second_type_id, 10, true);
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type),
                  comparison.canComparePartialTypes(&first_type, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type, &second_type_nullable));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type_nullable));
      }
    }
  
    for (const TypeID first_type_id : {kChar, kVarChar}) {
      const Type &first_type = TypeFactory::GetType(first_type_id, 10, false);
      const Type &first_type_nullable = TypeFactory::GetType(first_type_id, 10, true);
  
      for (const TypeID second_type_id
           : {kInt, kLong, kFloat, kDouble, kDate, kDatetime, kDatetimeInterval, kYearMonthInterval}) {
        const Type &second_type = TypeFactory::GetType(second_type_id, false);
        const Type &second_type_nullable = TypeFactory::GetType(second_type_id, true);
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type),
                  comparison.canComparePartialTypes(&first_type, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type, &second_type_nullable));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type_nullable));
      }
  
      for (const TypeID second_type_id : {kChar, kVarChar}) {
        const Type &second_type = TypeFactory::GetType(second_type_id, 10, false);
        const Type &second_type_nullable = TypeFactory::GetType(second_type_id, 10, true);
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type),
                  comparison.canComparePartialTypes(&first_type, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type, &second_type_nullable));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type));
        EXPECT_EQ(comparison.canCompareTypes(first_type_nullable, second_type_nullable),
                  comparison.canComparePartialTypes(&first_type_nullable, &second_type_nullable));
      }
    }
  }
  
  TEST_F(ComparisonTest, CanComparePartialTypesTest) {
    CheckBasicComparisonApplicableToPartialTypes(
        ComparisonFactory::GetComparison(ComparisonID::kEqual));
    CheckBasicComparisonApplicableToPartialTypes(
        ComparisonFactory::GetComparison(ComparisonID::kNotEqual));
    CheckBasicComparisonApplicableToPartialTypes(
        ComparisonFactory::GetComparison(ComparisonID::kLess));
    CheckBasicComparisonApplicableToPartialTypes(
        ComparisonFactory::GetComparison(ComparisonID::kLessOrEqual));
    CheckBasicComparisonApplicableToPartialTypes(
        ComparisonFactory::GetComparison(ComparisonID::kGreater));
    CheckBasicComparisonApplicableToPartialTypes(
        ComparisonFactory::GetComparison(ComparisonID::kGreaterOrEqual));
  }
  
  TEST_F(ComparisonTest, PatternMatchingComparisonTest) {
    const Comparison &like_comp =
        ComparisonFactory::GetComparison(ComparisonID::kLike);
    const Comparison &not_like_comp =
        ComparisonFactory::GetComparison(ComparisonID::kNotLike);
    const Comparison &regex_comp =
        ComparisonFactory::GetComparison(ComparisonID::kRegexMatch);
    const Comparison &not_regex_comp =
        ComparisonFactory::GetComparison(ComparisonID::kNotRegexMatch);
  
    // Check canCompareTypes method
    for (const TypeID tid1 : {kChar, kVarChar}) {
      const Type &t1 = TypeFactory::GetType(tid1, 10, false);
      for (const TypeID tid2 : {kChar, kVarChar}) {
        const Type &t2 = TypeFactory::GetType(tid2, 10, false);
        EXPECT_TRUE(like_comp.canCompareTypes(t1, t2));
        EXPECT_TRUE(not_like_comp.canCompareTypes(t1, t2));
        EXPECT_TRUE(regex_comp.canCompareTypes(t1, t2));
        EXPECT_TRUE(not_regex_comp.canCompareTypes(t1, t2));
      }
    }
  
    string text = "abc123\nXYZ\\n 127.0.0.1 @xxx.com";
    vector<string> matched_like_patterns = {
        "%",
        "abc%",
        "%127.0.0.1%",
        "%123_XYZ%",
        "%\\% @%",
        "abc%_%_3%" };
    vector<string> not_matched_like_patterns = {
        "abc",
        "%127__0.0.1%",
        "abc%_%_2",
        "%\\nXYZ%" };
    vector<string> matched_regex_patterns = {
        "(.|\n)*",
        "abc[^x]+xxx\\..*",
        "(.|\n)*\\d+\\.\\d+\\.\\d+\\.\\d+.*",
        "(.|\n)*[a-zA-Z]{3}.*" };
    vector<string> not_matched_regex_patterns = {
        ".*",
        ".*\\d{4}.*",
        "(.|\n)*XYZ\n .*" };
  
    for (const TypeID tid1 : {kChar, kVarChar}) {
      const Type &t1 = TypeFactory::GetType(tid1, 32, false);
      const TypedValue text_value(tid1, text.c_str(), text.size() + 1);
  
      for (const TypeID tid2 : {kChar, kVarChar}) {
        const Type &t2 = TypeFactory::GetType(tid2, 32, false);
  
        for (const string &sp : matched_like_patterns) {
          const TypedValue pattern_value(tid2, sp.c_str(), sp.size() + 1);
          EXPECT_TRUE(like_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
          EXPECT_FALSE(not_like_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
        }
  
        for (const string &sp : not_matched_like_patterns) {
          const TypedValue pattern_value(tid2, sp.c_str(), sp.size() + 1);
          EXPECT_FALSE(like_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
          EXPECT_TRUE(not_like_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
        }
  
        for (const string &sp : matched_regex_patterns) {
          const TypedValue pattern_value(tid2, sp.c_str(), sp.size() + 1);
          EXPECT_TRUE(regex_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
          EXPECT_FALSE(not_regex_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
        }
  
        for (const string &sp : not_matched_regex_patterns) {
          const TypedValue pattern_value(tid2, sp.c_str(), sp.size() + 1);
          EXPECT_FALSE(regex_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
          EXPECT_TRUE(not_regex_comp.compareTypedValuesChecked(text_value, t1, pattern_value, t2));
        }
      }
    }
  }
  
  }  // namespace quickstep
  