/**
 * Copyright (c) 2021 OceanBase
 * OceanBase CE is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#define USING_LOG_PREFIX SQL_ENG

#include "sql/engine/expr/ob_expr_operator.h"
#include <math.h>
#include "lib/oblog/ob_log.h"
//#include "sql/engine/expr/ob_expr_promotion_util.h"
#include "sql/engine/expr/ob_expr_result_type_util.h"
#include "sql/engine/expr/ob_expr_less_than.h"
#include "sql/engine/ob_physical_plan_ctx.h"
#include "sql/engine/expr/ob_expr_add.h"
#include "sql/engine/expr/ob_expr_minus.h"
#include "sql/engine/expr/ob_expr_subquery_ref.h"
#include "sql/engine/expr/ob_expr_null_safe_equal.h"
#include "sql/session/ob_sql_session_info.h"
#include "sql/engine/expr/ob_infix_expression.h"
#include "sql/code_generator/ob_static_engine_expr_cg.h"
#include "sql/engine/subquery/ob_subplan_filter_op.h"
#include "sql/engine/ob_exec_context.h"
#include "sql/engine/expr/ob_expr_util.h"

namespace oceanbase {
using namespace common;
using namespace common::number;
using namespace oceanbase::lib;
namespace sql {
static const int32_t DAYS_PER_YEAR[2] = {365, 366};
static const int8_t DAYS_PER_MON[2][12 + 1] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};

#define IS_LEAP_YEAR(y) ((((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) ? 1 : 0)

const char* ObExprTRDateFormat::FORMATS_TEXT[FORMAT_MAX_TYPE] = {"SYYYY",
    "YYYY",
    "YEAR",
    "SYEAR",
    "YYY",
    "YY",
    "Y",
    "IYYY",
    "IY",
    "I",
    "Q",
    "MONTH",
    "MON",
    "MM",
    "RM",
    "WW",
    "IW",
    "W",
    "DDD",
    "DD",
    "J",
    "DAY",
    "DY",
    "D",
    "HH",
    "HH12",
    "HH24",
    "MI",
    "CC",
    "SCC"};

uint64_t ObExprTRDateFormat::FORMATS_HASH[FORMAT_MAX_TYPE] = {0};

OB_SERIALIZE_MEMBER(ObFuncInputType, calc_meta_, max_length_, flag_);
OB_SERIALIZE_MEMBER_INHERIT(
    ObExprResType, ObObjMeta, accuracy_, calc_accuracy_, calc_type_, res_flags_, row_calc_cmp_types_);

const ObTimeZoneInfo* get_timezone_info(const ObSQLSessionInfo* session)
{
  return TZ_INFO(session);
}
const common::ObString* get_nls_formats(const ObSQLSessionInfo* session)
{
  return GET_NLS_FORMATS(session);
}
const common::ObObjPrintParams get_obj_print_params(const ObSQLSessionInfo* session)
{
  return CREATE_OBJ_PRINT_PARAM(session);
}

int64_t get_cur_time(ObPhysicalPlanCtx* phy_plan_ctx)
{
  return NULL != phy_plan_ctx ? phy_plan_ctx->get_cur_time().get_datetime() : 0;
}

int get_tz_offset(const ObTimeZoneInfo* tz_info, int64_t& offset)
{
  int ret = OB_SUCCESS;
  offset = 0;
  int32_t tmp_offset = 0;
  if (NULL != tz_info && OB_SUCC(tz_info->get_timezone_offset(0, tmp_offset))) {
    offset = SEC_TO_USEC(tmp_offset);
  }
  return ret;
}

int ObExprOperator::assign(const ObExprOperator& other)
{
  int ret = OB_SUCCESS;
  if (OB_LIKELY(this != &other)) {
    if (OB_FAIL(result_type_.assign(other.result_type_))) {
      LOG_WARN("copy result_type failed", K(ret));
    } else if (OB_FAIL(input_types_.assign(other.input_types_))) {
      LOG_WARN("copy input_types failed", K(ret));
    } else {
      this->magic_ = other.magic_;
      this->id_ = other.id_;
      this->row_dimension_ = other.row_dimension_;
      this->real_param_num_ = other.real_param_num_;
      this->is_called_in_sql_ = other.is_called_in_sql_;
      this->extra_serialize_ = other.extra_serialize_;
    }
  }
  return ret;
}

int ObExprOperator::cg_expr(ObExprCGCtx&, const ObRawExpr& raw_expr, ObExpr&) const
{
  int ret = STATIC_ENG_NOT_IMPLEMENT;
  LOG_INFO("not implemented in sql static typing engine, "
           "will retry the old engine automatically",
      K(ret),
      K(raw_expr));
  return ret;
}

// check function pointer in vtable to detect cg_expr() is overwrite or not.
bool ObExprOperator::is_default_expr_cg() const
{
  static ObArenaAllocator alloc;
  static ObExprOperator base(alloc, T_NULL, "fake_null_operator", 0);
  typedef int (ObExprOperator::*CGFunc)(ObExprCGCtx & op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const;
  union {
    CGFunc func_;
    int64_t val_;
    int64_t values_[2];
  } func_val;
  static_assert(sizeof(int64_t) * 2 == sizeof(CGFunc), "size mismatch");
  func_val.func_ = &ObExprOperator::cg_expr;
  const int64_t func_idx = func_val.val_ / sizeof(void *);
  return (*(void***)(&base))[func_idx] == (*(void***)(this))[func_idx];
}

int ObExprOperator::set_input_types(const ObIExprResTypes& expr_types)
{
  int ret = OB_SUCCESS;
  input_types_.reset();
  if (OB_FAIL(input_types_.reserve(expr_types.count()))) {
    LOG_WARN("fail to init input types", K(ret));
  }
  for (int64_t i = 0; OB_SUCC(ret) && i < expr_types.count(); ++i) {
    const ObExprResType& t = expr_types.at(i);
    ObFuncInputType type(t.get_calc_meta(), t.get_length(), t.get_result_flag());
    ret = input_types_.push_back(type);
  }

  return ret;
}

ObObjType ObExprOperator::get_calc_cast_type(ObObjType param_type, ObObjType calc_type)
{
  ObObjType cast_type = param_type;
  if (ObNullType == param_type || ObNullType == calc_type) {
    cast_type = param_type;
  } else if (ObMaxType == param_type || ObMaxType == calc_type) {
    cast_type = param_type;
  } else if (ob_is_integer_type(param_type) && ob_is_integer_type(calc_type)) {
    cast_type = param_type;
    //  } else if (ob_is_enumset_tc(param_type) && ob_is_enumset_numeric_type(calc_type)) {
    //    cast_type = param_type;
  } else if (param_type != calc_type) {
    cast_type = calc_type;
  }
  return cast_type;
}

int OB_INLINE ObExprOperator::cast_operand_type(
    common::ObObj& res_obj, const ObFuncInputType& res_type, common::ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  // check if need to cast
  const ObObjType param_type = res_obj.get_type();
  const ObObjType calc_type = res_type.get_calc_type();
  const ObCollationType param_collation_type = res_obj.get_collation_type();
  const ObCollationType calc_collation_type = res_type.get_calc_meta().get_collation_type();
  if (OB_LIKELY((calc_type != param_type && ObNullType != param_type) ||
                (ob_is_string_or_lob_type(param_type) && ob_is_string_or_lob_type(calc_type) &&
                    (lib::is_oracle_mode() || param_collation_type != calc_collation_type)))) {
    LOG_DEBUG(
        "need cast operand", K(res_type), K(res_type.get_calc_meta().get_scale()), K(res_obj), K(res_obj.get_scale()));
    ObCastMode cast_mode = get_cast_mode();
    // In PAD expression, we need add COLUMN_CONVERT to cast mode when cast is
    // from bit to binary and column convert is set in column_conv_ctx_. The COLUMN_CONVERT
    // cast mode is used in bit_to_string to decide which cast way is appropriate.
    if (OB_UNLIKELY(T_FUN_PAD == get_type() && ob_is_bit_tc(param_type) &&
                    ob_is_varbinary_type(calc_type, calc_collation_type) &&
                    CM_IS_COLUMN_CONVERT(expr_ctx.column_conv_ctx_.cast_mode_))) {
      cast_mode |= CM_COLUMN_CONVERT;
    }

    if (ob_is_string_or_lob_type(res_type.get_calc_type()) && res_type.is_zerofill()) {
      // For zerofilled string
      ObZerofillInfo zf_info(true, res_type.get_length());
      EXPR_DEFINE_CAST_CTX_ZF(expr_ctx, cast_mode, &zf_info);
      if (OB_FAIL(ObObjCaster::to_type(res_type.get_calc_type(), cast_ctx, res_obj, res_obj))) {
        LOG_WARN("fail to convert type", K(ret), K(res_type.get_calc_type()), K(res_obj));
      }
    } else {
      /* type collatino convertion rules:
       * 1.set target collation in cast_ctx.utf8mb4 is the default value
       * 2.if both src and dest type are ObStirngTC, rule:
       *   a.if calc_collation is set, use it
       *   b.if calc_collation not set, use src collation
       */
      EXPR_DEFINE_CAST_CTX(expr_ctx, cast_mode);
      if (ob_is_string_or_lob_type(calc_type)) {
        if (ob_is_string_or_lob_type(param_type)) {
          if (CS_TYPE_INVALID != calc_collation_type) {
            cast_ctx.dest_collation_ = calc_collation_type;
          } else if (share::is_mysql_mode() && CS_TYPE_INVALID != param_collation_type) {
            cast_ctx.dest_collation_ = param_collation_type;
          }
        } else {
          if (CS_TYPE_INVALID != calc_collation_type) {
            cast_ctx.dest_collation_ = calc_collation_type;
          } else {
            cast_ctx.dest_collation_ = ObCharset::get_default_collation_oracle(CHARSET_UTF8MB4);
          }
        }
      }
      ObObj res_obj_copy = res_obj;
      cast_ctx.expect_obj_collation_ = cast_ctx.dest_collation_;
      if (OB_FAIL(ObObjCaster::to_type(res_type.get_calc_type(), cast_ctx, res_obj_copy, res_obj))) {
        LOG_WARN("fail to convert type", K(ret), K(res_type.get_calc_type()), K(res_obj));
      }
      if (OB_SUCC(ret) && ob_is_string_or_lob_type(param_type) && ob_is_string_or_lob_type(calc_type) &&
          param_collation_type != calc_collation_type && calc_collation_type != CS_TYPE_INVALID) {
        res_obj.set_collation_type(calc_collation_type);
      }
    }
  }
  return ret;
}

int ObExprOperator::cast_operand_type(ObObj* params, const int64_t param_num, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  const bool fallback_old_mode = false;
  if (OB_ISNULL(params) || OB_UNLIKELY(param_num < 0) || OB_UNLIKELY(param_num != real_param_num_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("NULL param or invalid number", K(params), K(param_num), K(real_param_num_), K(ret));
  } else if (fallback_old_mode) {
    // fallback
  }  else if (OB_UNLIKELY(real_param_num_ <= 0 ||
              0 == input_types_.count() ||
              NOT_ROW_DIMENSION != row_dimension_ ||
              T_FUN_SYS_TIMESTAMP_NVL == get_type() /*||
              T_FUN_SYS_CAST == get_type() ||
              T_OP_CASE == get_type() ||
              T_OP_ARG_CASE == get_type() ||
              T_FUN_COLUMN_CONV == get_type()*/)) {
    // skip
  } else if (OB_UNLIKELY(input_types_.count() != real_param_num_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("real_param_num_ is wrong", K(input_types_.count()), K_(real_param_num));
  } else {
    // convert param types only if src and dest are different types
    for (int64_t i = 0; OB_SUCC(ret) && i < real_param_num_; ++i) {
      if (OB_FAIL(cast_operand_type(params[i], input_types_.at(i), expr_ctx))) {
        LOG_WARN("cast failed", K(ret), K(params[i]), K(input_types_.at(i)));
      }
    }
  }

  return ret;
}

int ObExprOperator::call(ObObj* stack, int64_t& stack_size, ObExprCtx& expr_ctx) const

{
  ObObj result;
  int ret = OB_SUCCESS;
  if (OB_ISNULL(stack) || OB_ISNULL(this) || OB_UNLIKELY(real_param_num_ < 0)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("stack or this is null or stack_size is wrong", K(stack), K(this), K(real_param_num_), K(ret));
  } else if (OB_UNLIKELY(real_param_num_ > stack_size)) {
    ret = OB_INVALID_ARGUMENT_NUM;
    LOG_WARN("wrong number of input arguments on stack", K(real_param_num_), K(stack_size), K(ret));
  } else if (OB_LIKELY(row_dimension_ != NOT_ROW_DIMENSION)) {
    int32_t param_num = real_param_num_ * row_dimension_;
    if (OB_UNLIKELY(param_num > stack_size)) {
      ret = OB_INVALID_ARGUMENT;
      LOG_WARN("wrong number of input arguments on stack", K(param_num), K(stack_size));
      // vector operator
    } else if (OB_FAIL(this->calc_resultN(result, &stack[stack_size - param_num], param_num, expr_ctx))) {
      LOG_WARN("fail to calc resultN", K(ret), K(param_num), K(stack_size));
    } else {
      stack[stack_size - param_num] = result;
      stack_size -= (param_num - 1);
    }
  } else if (operand_auto_cast_ &&
             OB_FAIL(cast_operand_type(stack + stack_size - real_param_num_, real_param_num_, expr_ctx))) {
    LOG_WARN("fail convert operand types", K(stack_size), K(ret));
  } else {
    if (OB_UNLIKELY(param_num_ > 0 && param_num_ != real_param_num_)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_ERROR("bug! param_num is not equal than real_param_num",
          K_(name),
          K(get_type_name(type_)),
          K_(type),
          K_(param_num),
          K_(real_param_num),
          K_(row_dimension),
          K(input_types_.count()),
          K(stack_size),
          K(ret));
    }
    if (OB_UNLIKELY(real_param_num_ < 0)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_ERROR("bug! real_param_num is less than 0",
          K_(name),
          K(get_type_name(type_)),
          K_(type),
          K_(param_num),
          K_(real_param_num),
          K_(row_dimension),
          K(input_types_.count()),
          K(stack_size),
          K(ret));
    }
    if (OB_SUCC(ret)) {
      switch (param_num_) {
        case 0: {
          if (OB_FAIL(this->calc_result0(result, expr_ctx))) {
            LOG_WARN("fail to calc result0", K(ret), K(stack_size));
          } else {
            stack[stack_size] = result;
            ++stack_size;
          }
          break;
        }
        case 1: {
          if (OB_FAIL(this->calc_result1(result, stack[stack_size - 1], expr_ctx))) {
            LOG_WARN("fail to calc result1", K(ret), K(stack_size), N_FUNC, get_type_name(type_));
          } else {
            stack[stack_size - 1] = result;
          }
          break;
        }
        case 2: {
          if (OB_FAIL(this->calc_result2(result, stack[stack_size - 2], stack[stack_size - 1], expr_ctx))) {
            LOG_WARN("fail to calc result2", K(ret), K(stack_size), N_FUNC, get_type_name(type_));
          } else {
            stack[stack_size - 2] = result;
            stack_size--;
          }
          break;
        }
        case 3: {
          // org mode
          if (OB_FAIL(this->calc_result3(
                  result, stack[stack_size - 3], stack[stack_size - 2], stack[stack_size - 1], expr_ctx))) {
            LOG_WARN("fail to calc result3", K(ret), K(stack_size), N_FUNC, get_type_name(type_));
          } else {
            stack[stack_size - 3] = result;
            stack_size -= 2;
          }
          break;
        }
        default: {
          if (OB_FAIL(this->calc_resultN(result, &stack[stack_size - real_param_num_], real_param_num_, expr_ctx))) {
            LOG_WARN("fail to calc resultN", K(ret), K(stack_size), N_FUNC, get_type_name(type_));
          } else {
            stack[stack_size - real_param_num_] = result;
            stack_size -= real_param_num_ - 1;
          }
          break;
        }
      }  // end switch
      if (!stack[stack_size - 1].is_bit()) {
        stack[stack_size - 1].set_scale(result_type_.get_scale());
      }
    }
  }
  return ret;
}

int ObExprOperator::eval(
    common::ObExprCtx& expr_ctx, common::ObObj& val, common::ObObj* params, int64_t param_num) const
{
  int ret = OB_SUCCESS;
  // some expr (e.g.: subtime) rely on the default value of ObObj, so we construct it again.
  // (see ObExprOperator::calc(), use an new stack ObObj variable and assign to val finally).
  new (&val) ObObj();
  if (OB_ISNULL(params) || OB_UNLIKELY(param_num < 0)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret));
  } else if (OB_UNLIKELY(row_dimension_ != NOT_ROW_DIMENSION)) {
    // row operator
    if (OB_FAIL(calc_resultN(val, params, param_num, expr_ctx))) {
      LOG_WARN("calc result failed", K(ret), K(name_), K(get_type_name(type_)));
    }
  } else if (!is_param_lazy_eval() && operand_auto_cast_ && OB_FAIL(cast_operand_type(params, param_num, expr_ctx))) {
    LOG_WARN("cast operand failed", K(ret));
  } else {
    if (OB_UNLIKELY(param_num != real_param_num_) || OB_UNLIKELY(param_num_ > 0 && param_num_ != real_param_num_)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_ERROR("wrong param num", K(ret), K(param_num), K(param_num_), K(real_param_num_));
    } else {
      // Must use %param_num_, because it will set to negative number to indicate that
      // we should call calc_resultN here. e.g.: concat
      switch (param_num_) {
        case 0: {
          ret = calc_result0(val, expr_ctx);
          break;
        }
        case 1: {
          ret = calc_result1(val, params[0], expr_ctx);
          break;
        }
        case 2: {
          ret = calc_result2(val, params[0], params[1], expr_ctx);
          break;
        }
        case 3: {
          ret = calc_result3(val, params[0], params[1], params[2], expr_ctx);
          break;
        }
        default: {
          ret = calc_resultN(val, params, param_num, expr_ctx);
        }
      }
      if (OB_FAIL(ret)) {
        LOG_WARN("calc result failed", K(ret), K(param_num_), K(name_), K(get_type_name(type_)));
      }
      if (!val.is_bit() && !val.is_ext()) {
        val.set_scale(result_type_.get_scale());
      }
    }
  }
  return ret;
}

int ObExprOperator::param_eval(common::ObExprCtx& expr_ctx, const common::ObObj& param, const int64_t param_index) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(expr_ctx.infix_expr_)) {
    // do nothing for postfix expression
  } else {
    if (OB_FAIL(expr_ctx.infix_expr_->param_eval(expr_ctx, param))) {
      LOG_WARN("param eval failed", K(ret));
    } else if (OB_LIKELY(row_dimension_ == NOT_ROW_DIMENSION) && OB_LIKELY(T_FUN_SYS_TIMESTAMP_NVL != type_) &&
               OB_LIKELY(param_index) < input_types_.count()) {
      // skip row operator and T_FUN_SYS_TIMESTAMP_NVL. (see cast_operand_type())
      if (operand_auto_cast_ &&
          OB_FAIL(cast_operand_type(const_cast<ObObj&>(param), input_types_.at(param_index), expr_ctx))) {
        LOG_WARN("cast failed", K(ret), K(param), K(input_types_.at(param_index)));
      }
    }
  }
  return ret;
}

bool ObExprOperator::is_valid_nls_param(const common::ObString& nls_param_str)
{
  bool bret = false;
  if (!nls_param_str.empty() && NULL != nls_param_str.find('=')) {
    static const common::ObString DEFAULT_VALUE_CALENDAR("GREGORIAN");
    static const common::ObString DEFAULT_VALUE_DATE_LANGUAGE("AMERICAN");
    static const common::ObString DEFAULT_VALUE_LANGUAGE("AMERICAN");
    static const common::ObString DEFAULT_VALUE_NUMERIC_CHARACTERS(".,");
    static const common::ObString DEFAULT_VALUE_SORT("BINARY");
    static const common::ObString DEFAULT_VALUE_COMP("BINARY");
    static const common::ObString DEFAULT_VALUE_CURRENCY("$");
    static const common::ObString DEFAULT_VALUE_ISO_CURRENCY("AMERICA");
    static const common::ObString DEFAULT_VALUE_DUAL_CURRENCY("$");

    static const common::ObString DEFAULT_NAME_CALENDAR(share::OB_SV_NLS_CALENDAR);
    static const common::ObString DEFAULT_NAME_DATE_LANGUAGE(share::OB_SV_NLS_DATE_LANGUAGE);
    static const common::ObString DEFAULT_NAME_LANGUAGE(share::OB_SV_NLS_LANGUAGE);
    static const common::ObString DEFAULT_NAME_NUMERIC_CHARACTERS(share::OB_SV_NLS_NUMERIC_CHARACTERS);
    static const common::ObString DEFAULT_NAME_SORT(share::OB_SV_NLS_SORT);
    static const common::ObString DEFAULT_NAME_COMP(share::OB_SV_NLS_COMP);
    static const common::ObString DEFAULT_NAME_CURRENCY(share::OB_SV_NLS_CURRENCY);
    static const common::ObString DEFAULT_NAME_ISO_CURRENCY(share::OB_SV_NLS_ISO_CURRENCY);
    static const common::ObString DEFAULT_NAME_DUAL_CURRENCY(share::OB_SV_NLS_DUAL_CURRENCY);

    ObString value_str = const_cast<common::ObString&>(nls_param_str).trim();
    ObString name_str = value_str.split_on('=');
    name_str = name_str.trim();
    value_str = value_str.trim();
    if (!name_str.empty() && !value_str.empty()) {
      if ((0 == name_str.case_compare(DEFAULT_NAME_CALENDAR) && 0 == value_str.case_compare(DEFAULT_VALUE_CALENDAR)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_DATE_LANGUAGE) &&
              0 == value_str.case_compare(DEFAULT_VALUE_DATE_LANGUAGE)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_LANGUAGE) && 0 == value_str.case_compare(DEFAULT_VALUE_LANGUAGE)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_NUMERIC_CHARACTERS) &&
              0 == value_str.case_compare(DEFAULT_VALUE_NUMERIC_CHARACTERS)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_SORT) && 0 == value_str.case_compare(DEFAULT_VALUE_SORT)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_COMP) && 0 == value_str.case_compare(DEFAULT_VALUE_COMP)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_CURRENCY) && 0 == value_str.case_compare(DEFAULT_VALUE_CURRENCY)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_ISO_CURRENCY) &&
              0 == value_str.case_compare(DEFAULT_VALUE_ISO_CURRENCY)) ||
          (0 == name_str.case_compare(DEFAULT_NAME_DUAL_CURRENCY) &&
              0 == value_str.case_compare(DEFAULT_VALUE_DUAL_CURRENCY))) {
        bret = true;
      }
    }
  }
  return bret;
}

/*
 * get default collation type from session for string type
 * do not use this function for non-string type
 * */
ObCollationType ObExprOperator::get_default_collation_type(ObObjType type, const ObBasicSessionInfo& session_info)
{
  ObCollationType collation_type = CS_TYPE_INVALID;
  if (ob_is_string_or_lob_type(type)) {
    if (share::is_mysql_mode()) {
      collation_type = static_cast<ObCollationType>(session_info.get_local_collation_connection());
    } else {
      if (ob_is_nstring(type)) {
        // nvarchar2 nchar nclob
        collation_type = session_info.get_nls_collation_nation();
      } else {
        // varchar2 char clob
        collation_type = session_info.get_nls_collation();
      }
    }
  }
  return collation_type;
}

OB_SERIALIZE_MEMBER(ObExprOperator, row_dimension_, real_param_num_, result_type_, input_types_, id_, extra_serialize_);

int ObExprOperator::aggregate_collations(
    ObObjMeta& type, const ObObjMeta* types, int64_t param_num, uint32_t flags, const ObCollationType conn_coll_type)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num <= 0)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else {
    ObCollationType coll_type = types[0].get_collation_type();
    ObCollationLevel coll_level = types[0].get_collation_level();
    for (int64_t i = 1; OB_SUCC(ret) && i < param_num; ++i) {
      ret = ObCharset::aggregate_collation(
          coll_level, coll_type, types[i].get_collation_level(), types[i].get_collation_type(), coll_level, coll_type);
    }
    if (OB_SUCC(ret)) {
      if (OB_UNLIKELY((flags & OB_COLL_DISALLOW_NONE) && CS_LEVEL_NONE == coll_level)) {
        // @todo () correct error code is OB_CANT_AGGREGATE_NCOLLATIONS
        ret = OB_CANT_AGGREGATE_2COLLATIONS;
      }
    }
    if (OB_SUCC(ret)) {
      /* If all arguments were numbers, reset to @@collation_connection */
      if (OB_UNLIKELY((flags & OB_COLL_ALLOW_NUMERIC_CONV) && CS_LEVEL_NUMERIC == coll_level)) {
        coll_type = conn_coll_type;
        coll_level = CS_LEVEL_COERCIBLE;
        // MySQL need charset converter here. We consider only two charset(binary
        // and utf8mb4), so no conversion is actually needed
      } else {
      }
      if (OB_SUCC(ret)) {
        type.set_collation_type(coll_type);
        type.set_collation_level(coll_level);
      } else {
      }
    } else {
    }
    if (OB_CANT_AGGREGATE_2COLLATIONS == ret) {
      if (3 == param_num) {
        ret = OB_CANT_AGGREGATE_3COLLATIONS;
      } else if (3 < param_num) {
        ret = OB_CANT_AGGREGATE_NCOLLATIONS;
      }
    }
  }
  return ret;
}

int ObExprOperator::aggregate_charsets_for_string_result(
    ObObjMeta& type, const ObObjMeta* types, int64_t param_num, const ObCollationType conn_coll_type)
{
  uint32_t flags = OB_COLL_ALLOW_NUMERIC_CONV;
  return aggregate_charsets(type, types, param_num, flags, conn_coll_type);
}

int ObExprOperator::aggregate_charsets_for_string_result(
    ObExprResType& type, const ObExprResType* types, int64_t param_num, const ObCollationType conn_coll_type)
{
  uint32_t flags = OB_COLL_ALLOW_NUMERIC_CONV;
  return aggregate_charsets(type, types, param_num, flags, conn_coll_type);
}

int ObExprOperator::aggregate_charsets_for_comparison(
    ObObjMeta& type, const ObObjMeta* types, int64_t param_num, const ObCollationType conn_coll_type)
{
  uint32_t flags = OB_COLL_DISALLOW_NONE;
  return aggregate_charsets(type, types, param_num, flags, conn_coll_type);
}

int ObExprOperator::aggregate_charsets_for_comparison(
    ObExprResType& type, const ObExprResType* types, int64_t param_num, const ObCollationType conn_coll_type)
{
  uint32_t flags = OB_COLL_DISALLOW_NONE;
  return aggregate_charsets(type.get_calc_meta(), types, param_num, flags, conn_coll_type);
}

int ObExprOperator::aggregate_charsets_for_string_result_with_comparison(
    ObObjMeta& type, const ObObjMeta* types, int64_t param_num, const ObCollationType conn_coll_type)
{
  uint32_t flags = OB_COLL_DISALLOW_NONE | OB_COLL_ALLOW_NUMERIC_CONV;
  return aggregate_charsets(type, types, param_num, flags, conn_coll_type);
}

int ObExprOperator::aggregate_charsets_for_string_result_with_comparison(
    ObObjMeta& type, const ObExprResType* types, int64_t param_num, const ObCollationType coll_type)
{
  uint32_t flags = OB_COLL_DISALLOW_NONE | OB_COLL_ALLOW_NUMERIC_CONV;
  return aggregate_charsets(type, types, param_num, flags, coll_type);
}

int ObExprOperator::aggregate_charsets(
    ObObjMeta& type, const ObObjMeta* types, int64_t param_num, uint32_t flags, const ObCollationType conn_coll_type)
{
  return aggregate_collations(type, types, param_num, flags, conn_coll_type);
}

int ObExprOperator::aggregate_charsets(ObObjMeta& type, const ObExprResType* types, int64_t param_num, uint32_t flags,
    const ObCollationType conn_coll_type)
{
  int ret = OB_SUCCESS;
  CK(OB_NOT_NULL(types), !OB_UNLIKELY(param_num < 1));

  if (OB_SUCC(ret)) {
    ObSEArray<ObObjMeta, 16> coll_types;
    ObObjMeta coll;
    for (int i = 0; OB_SUCC(ret) && i < param_num; ++i) {
      coll.reset();
      coll.set_collation_type(types[i].get_collation_type());
      coll.set_collation_level(types[i].get_collation_level());
      ret = coll_types.push_back(coll);
    }  // end for

    OZ(aggregate_charsets(type, &coll_types.at(0), param_num, flags, conn_coll_type));
  }
  return ret;
}

int ObExprOperator::aggregate_string_type_and_charset_oracle(const ObBasicSessionInfo& session,
    const ObIArray<ObExprResType*>& params, ObExprResType& result, bool prefer_var_len_char)
{
  int ret = OB_SUCCESS;

  bool has_clob_type = false;
  bool has_character_type = false;
  bool has_varying_len_string_type = false;
  bool has_nation_string_type = false;

  CK(params.count() > 0);

  for (int64_t i = 0; OB_SUCC(ret) && i < params.count(); ++i) {
    const ObExprResType* param_meta = params.at(i);
    CK(OB_NOT_NULL(param_meta));
    if (OB_SUCC(ret)) {
      if (param_meta->is_string_or_lob_locator_type()) {
        has_clob_type |= param_meta->is_clob();
        has_clob_type |= param_meta->is_clob_locator();
        has_character_type |= param_meta->is_character_type();
        has_nation_string_type |= param_meta->is_nstring();
        has_varying_len_string_type |= param_meta->is_varying_len_char_type();
      } else {
        has_varying_len_string_type = true;
      }
    }
  }

  // 1. deduce type + charset
  ObObjType result_type = ObMaxType;
  ObCollationType result_charset = CS_TYPE_INVALID;

  if (OB_SUCC(ret)) {
    if (has_clob_type) {
      if (has_nation_string_type) {
        ret = OB_NOT_SUPPORTED;
        LOG_USER_ERROR(OB_NOT_SUPPORTED, "type nclob");
      } else {
        result_type = ObLongTextType;
        result_charset = session.get_nls_collation();
      }
    } else if (has_character_type) {
      if (has_nation_string_type) {
        result_type = has_varying_len_string_type || prefer_var_len_char ? ObNVarchar2Type : ObNCharType;
        result_charset = session.get_nls_collation_nation();
      } else {
        result_type = has_varying_len_string_type || prefer_var_len_char ? ObVarcharType : ObCharType;
        result_charset = session.get_nls_collation();
      }
    } else {  // blob and other types
      result_type = ObVarcharType;
      result_charset = session.get_nls_collation();
    }
  }

  if (OB_SUCC(ret)) {
    result.set_type_simple(result_type);
    if (ob_is_large_text(result_type)) {
      result.set_lob_inrow();
    }
    result.set_collation_type(result_charset);
    result.set_collation_level(CS_LEVEL_IMPLICIT);
  }

  //. deduce length sematics
  ObLengthSemantics result_ls = LS_INVALIED;

  if (OB_SUCC(ret)) {
    if (result.is_nstring() || result.is_clob() || result.is_clob_locator()) {
      result_ls = LS_CHAR;
    } else {
      bool is_all_the_same = true;
      for (int64_t i = 0; is_all_the_same && i < params.count(); ++i) {
        const ObExprResType* param_meta = params.at(i);
        if (param_meta->is_string_or_lob_locator_type()) {
          ObLengthSemantics curr_ls = param_meta->get_length_semantics();
          if (LS_INVALIED == result_ls) {
            result_ls = curr_ls;
          } else {
            is_all_the_same = (result_ls == curr_ls);
          }
        }
      }
      if (!is_all_the_same || LS_INVALIED == result_ls) {
        result_ls = session.get_actual_nls_length_semantics();
      }
    }
  }

  if (OB_SUCC(ret)) {
    result.set_length_semantics(result_ls);
  }

  LOG_DEBUG("aggregate string charset", K(result), K(params));

  return ret;
}

int ObExprOperator::deduce_string_param_calc_type_and_charset(
    const ObBasicSessionInfo& session, const ObExprResType& result, ObIArray<ObExprResType*>& params)
{
  UNUSED(session);
  int ret = OB_SUCCESS;
  for (int64_t i = 0; OB_SUCC(ret) && i < params.count(); ++i) {
    ObExprResType* param_meta = params.at(i);
    CK(OB_NOT_NULL(param_meta));
    OX(param_meta->set_calc_meta(result));
  }
  return ret;
}

int ObExprOperator::is_same_kind_type_for_case(const ObExprResType& type1, const ObExprResType& type2, bool& match)
{
  int ret = OB_SUCCESS;
  match = false;
  if (OB_UNLIKELY(type1.get_type() >= ObMaxType || type2.get_type() >= ObMaxType)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("the wrong type", K(type1), K(type2), K(ret));
  } else {
    if (ob_is_null(type1.get_type()) || ob_is_null(type2.get_type())) {
      match = true;
    } else if (ob_is_varchar_char_type(type1.get_type(), type1.get_collation_type())) {
      match = ob_is_varchar_char_type(type2.get_type(), type2.get_collation_type());
    } else if (ob_is_numeric_type(type1.get_type())) {
      match = ob_is_numeric_type(type2.get_type());
    } else if (ob_is_datetime_tc(type1.get_type()) || ob_is_otimestampe_tc(type1.get_type())) {
      match = ob_is_datetime_tc(type2.get_type()) || ob_is_otimestampe_tc(type2.get_type());
    } else if (ob_is_blob(type1.get_type(), type1.get_collation_type())) {
      match = ob_is_blob(type2.get_type(), type2.get_collation_type());
    } else if (ob_is_text(type1.get_type(), type1.get_collation_type())) {
      match = ob_is_text(type2.get_type(), type2.get_collation_type());
    } else if (ob_is_varbinary_type(type1.get_type(), type1.get_collation_type())) {
      match = ob_is_varbinary_type(type2.get_type(), type2.get_collation_type());
    } else if (ob_is_raw(type1.get_type())) {
      match = ob_is_raw(type2.get_type());
    } else if (ob_is_interval_ym(type1.get_type())) {
      match = ob_is_interval_ym(type2.get_type());
    } else if (ob_is_interval_ds(type1.get_type())) {
      match = ob_is_interval_ds(type2.get_type());
    } else if (ob_is_nstring_type(type1.get_type())) {
      match = ob_is_nstring_type(type2.get_type());
    } else if (ob_is_urowid(type1.get_type())) {
      match = ob_is_urowid(type2.get_type());
    } else if (ob_is_blob_locator(type1.get_type(), type1.get_collation_type())) {
      match = ob_is_blob_locator(type2.get_type(), type2.get_collation_type());
    } else if (ob_is_clob_locator(type1.get_type(), type1.get_collation_type())) {
      match = ob_is_clob_locator(type2.get_type(), type2.get_collation_type());
    }
  }
  return ret;
}

// coaesce expr, case when expr
int ObExprOperator::aggregate_result_type_for_case(ObExprResType& type, const ObExprResType* types, int64_t param_num,
    const ObCollationType conn_coll_type, bool is_oracle_mode, const ObLengthSemantics default_length_semantics,
    const ObSQLSessionInfo* session, bool need_merge_type, bool skip_null)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1) || OB_ISNULL(session)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else if (1 == param_num && ob_is_enumset_tc(types[0].get_type())) {
  } else if (is_oracle_mode) {
    bool match = false;
    int64_t nth = OB_INVALID_ID;
    for (int64_t i = 0; OB_SUCC(ret) && OB_INVALID_ID == nth && i < param_num; ++i) {
      if (!ob_is_null(types[i].get_type())) {
        nth = i;
      }
    }
    nth = OB_INVALID_ID == nth ? 0 : nth;
    const ObExprResType& res_type = types[nth];
    for (int64_t i = 1; OB_SUCC(ret) && i < param_num; ++i) {
      if (OB_FAIL(ObExprOperator::is_same_kind_type_for_case(res_type, types[i], match))) {
        LOG_WARN("fail to judge same type", K(i), K(res_type), K(types[i]), K(ret));
      } else if (!match) {
        ret = OB_ERR_INVALID_TYPE_FOR_OP;
        LOG_WARN("fail to judge same type", K(i), K(res_type), K(types[i]), K(ret));
      }
    }
  }
  if (OB_SUCC(ret)) {
    if (OB_FAIL(aggregate_result_type_for_merge(type,
            types,
            param_num,
            conn_coll_type,
            is_oracle_mode,
            default_length_semantics,
            session,
            need_merge_type,
            skip_null))) {
      LOG_WARN("fail to aggregate result type", K(ret));
    } else if (ObFloatType == type.get_type() && !is_oracle_mode) {
      type.set_type(ObDoubleType);
    }
  }
  return ret;
}

int ObExprOperator::aggregate_result_type_for_merge(ObExprResType& type, const ObExprResType* types, int64_t param_num,
    const ObCollationType conn_coll_type, bool is_oracle_mode, const ObLengthSemantics default_length_semantics,
    const ObSQLSessionInfo* session, bool need_merge_type, bool skip_null)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1) || OB_ISNULL(session)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else if (1 == param_num && ob_is_enumset_tc(types[0].get_type())) {
    // this is for case when clause like case  when 1 then c1 end;
    type.set_type(ObVarcharType);
    type.set_collation_level(common::CS_LEVEL_IMPLICIT);
    type.set_collation_type(session->get_local_collation_connection());
  } else {
    ObObjType res_type = types[0].get_type();
    for (int64_t i = 1; OB_SUCC(ret) && i < param_num; ++i) {
      if (OB_FAIL(ObExprResultTypeUtil::get_merge_result_type(res_type, res_type, types[i].get_type()))) {
        // warn
      } else if (OB_UNLIKELY(ObMaxType == res_type)) {
        ret = OB_INVALID_ARGUMENT;  // not compatible input
        LOG_WARN("invalid argument. wrong type for merge", K(i), K(types[i].get_type()), K(ret));
      }
    }
    if (OB_SUCC(ret)) {
      type.set_type(res_type);
      if (ob_is_numeric_type(res_type)) {
        ret = aggregate_numeric_accuracy_for_merge(type, types, param_num, is_oracle_mode);
      } else if (ob_is_temporal_type(res_type) || ob_is_otimestamp_type(res_type)) {
        ret = aggregate_temporal_accuracy_for_merge(type, types, param_num);
      } else if (ob_is_string_type(res_type)) {
        if (OB_FAIL(aggregate_charsets_for_string_result(type, types, param_num, conn_coll_type))) {
        } else if (OB_FAIL(aggregate_max_length_for_string_result(
                       type, types, param_num, is_oracle_mode, default_length_semantics, need_merge_type, skip_null))) {
        } else { /*do nothing*/
        }
      } else if (ob_is_raw(res_type)) {
        type.set_collation_type(CS_TYPE_BINARY);
        type.set_collation_level(CS_LEVEL_NUMERIC);
      } else if (ob_is_interval_tc(res_type)) {
        ret = aggregate_accuracy_for_merge(type, types, param_num);
      }
    }
    LOG_DEBUG("merged type is", K(type), K(is_oracle_mode));
  }
  return ret;
}

int ObExprOperator::aggregate_max_length_for_string_result(ObExprResType& type, const ObExprResType* types,
    int64_t param_num, bool is_oracle_mode, const ObLengthSemantics default_length_semantics, bool need_merge_type,
    bool skip_null)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else {
    ObLength max_length = -1;
    ObLength max_length_char = -1;
    ObLength length = -1;
    ObLengthSemantics length_semantics = -1;
    int64_t byte_length_count = 0;
    int64_t char_length_count = 0;
    bool len_in_byte = false;
    int64_t mbmaxlen = 1;
    if (OB_FAIL(common::ObCharset::get_mbmaxlen_by_coll(CS_TYPE_UTF8MB4_GENERAL_CI, mbmaxlen))) {
      LOG_WARN("fail to get mbmaxlen", K(ret));
    } else if (OB_FAIL(ObCharset::get_aggregate_len_unit(type.get_collation_type(), len_in_byte))) {
      LOG_WARN("get aggregate len unit failed", K(ret), K(type));
    } else if (len_in_byte) {
      // binary
      for (int64_t i = 0; OB_SUCC(ret) && i < param_num; ++i) {
        if (ob_is_integer_type(types[i].get_type())) {
          length = MAX(ObAccuracy::DDL_DEFAULT_ACCURACY[types[i].get_type()].precision_, types[i].get_precision());
        } else {
          if (OB_FAIL(types[i].get_length_for_meta_in_bytes(length))) {
            LOG_WARN("get length failed", K(ret), K(types[i]));
          }
        }
        /* Oracle compatible: if prejudged result type is char and args' length are different, change result type to
         * varchar */
        LOG_DEBUG("cur len", K(length), K(max_length), K(types[i]));
        if (is_oracle_mode && need_merge_type &&
            ((ObCharType == type.get_type() && ObCharType == types[i].get_type()) ||
                (ob_is_nchar(type.get_type()) && ob_is_nchar(types[i].get_type()))) &&
            (max_length != -1) && (length != max_length)) {
          LOG_DEBUG("Merge type from Char to Varchar ", K(length), K(max_length), K(types[i]));
          type.set_type(ob_is_nchar(type.get_type()) ? ObNVarchar2Type : ObVarcharType);
        }
        /*no need to if(OB_SUCCE(ret)) here*/
        if (length > max_length) {
          if (types[i].is_null() && skip_null) {
            // skip
          } else {
            max_length = length;
          }
        }
      }  // end for
    } else {
      for (int64_t i = 0; OB_SUCC(ret) && i < param_num; ++i) {
        if (ob_is_integer_type(types[i].get_type())) {
          length = MAX(ObAccuracy::DDL_DEFAULT_ACCURACY[types[i].get_type()].precision_, types[i].get_precision());
          length_semantics = -1;
        } else {
          length = types[i].get_length();
          length_semantics = types[i].get_length_semantics();

          if (LS_BYTE == length_semantics) {
            byte_length_count++;
          } else if (LS_CHAR == length_semantics) {
            char_length_count++;
            if (length > max_length_char) {
              max_length_char = length;
            }
          }
        }
        /* Oracle compatible: if prejudged result type is char and args' length are different, change result type to
         * varchar */
        LOG_DEBUG("cur len", K(length), K(max_length), K(max_length_char), K(length_semantics), K(types[i]), K(type));
        if (is_oracle_mode && need_merge_type &&
            ((ObCharType == type.get_type() && ObCharType == types[i].get_type()) ||
                (ob_is_nchar(type.get_type()) && ob_is_nchar(types[i].get_type()))) &&
            (max_length != -1) && (length != max_length || (byte_length_count > 0 && char_length_count > 0))) {
          LOG_DEBUG("Merge type from Char to Varchar ", K(length), K(max_length), K(length_semantics), K(types[i]));
          type.set_type(ob_is_nchar(type.get_type()) ? ObNVarchar2Type : ObVarcharType);
        }
        if (length > max_length) {
          if (types[i].is_null() && skip_null) {
            // skip
          } else {
            max_length = length;
          }
        }
      }  // end for
    }
    // set length
    if (OB_SUCC(ret) && max_length < 0) {
      if (skip_null) {
        max_length = 0;
      } else {
        ret = OB_ERR_UNEXPECTED;
        LOG_ERROR("unexpected max length.", K(ret), K(max_length), K(param_num), K(len_in_byte));
      }
    }
    if (OB_SUCC(ret)) {
      if (is_oracle_mode && type.is_character_type()) {
        if (type.is_nstring()) {
          type.set_length_semantics(LS_CHAR);
        } else {
          if (byte_length_count > 0 && 0 == char_length_count) {
            type.set_length_semantics(LS_BYTE);
          } else if (char_length_count > 0 && 0 == byte_length_count) {
            type.set_length_semantics(LS_CHAR);
          } else {
            type.set_length_semantics(default_length_semantics);
          }
        }
        if (LS_CHAR == type.get_length_semantics()) {
          type.set_length(std::max(max_length_char, max_length));
        } else {
          type.set_length(std::max(static_cast<ObLength>(max_length_char * mbmaxlen), max_length));
        }
      } else {
        type.set_length(max_length);
      }
    }
  }
  return ret;
}

int ObExprOperator::aggregate_accuracy_for_merge(ObExprResType& type, const ObExprResType* types, int64_t param_num)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else {
    ObScale scale = -1;
    for (int64_t i = 0; OB_SUCC(ret) && i < param_num; ++i) {
      if (types[i].get_scale() > scale) {
        scale = types[i].get_scale();
      }
    }
    if (OB_UNLIKELY(scale < 0)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("unexpected scale.", K(ret), K(scale));
    } else {
      type.set_scale(scale);
    }
  }
  return ret;
}

int ObExprOperator::aggregate_temporal_accuracy_for_merge(
    ObExprResType& type, const ObExprResType* types, int64_t param_num)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else {
    ObScale scale = -1;
    for (int64_t i = 0; OB_SUCC(ret) && i < param_num; ++i) {
      if ((ob_is_temporal_type(types[i].get_type()) || ob_is_otimestamp_type(types[i].get_type())) &&
          types[i].get_scale() > scale) {
        scale = types[i].get_scale();
      }
    }
    if (OB_UNLIKELY(scale < 0)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("unexpected scale.", K(ret), K(scale));
    } else {
      type.set_scale(scale);
    }
  }
  return ret;
}

int ObExprOperator::aggregate_numeric_accuracy_for_merge(
    ObExprResType& type, const ObExprResType* types, int64_t param_num, bool is_oracle_mode)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else if (is_oracle_mode) {
    type.set_scale(ORA_NUMBER_SCALE_UNKNOWN_YET);
    type.set_precision(PRECISION_UNKNOWN_YET);
  } else {
    ObPrecision precision = 0;
    ObScale scale = 0;
    int16_t max_integer_digits = -1;
    int16_t max_decimal_digits = -1;
    bool has_real_type = false;
    for (int64_t i = 0; i < param_num && OB_SUCC(ret); ++i) {
      precision = PRECISION_UNKNOWN_YET;
      scale = SCALE_UNKNOWN_YET;
      if (OB_UNLIKELY(types[i].get_type() < ObNullType || types[i].get_type() >= ObMaxType)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_ERROR("unexpected error. wrong type", K(ret), K(types[i]));
      } else if (ob_is_real_type(types[i].get_type())) {
        /*
          create table sb(a float);
          create table sc(c year);
          insert sb values (1.5);
          insert sc values(1923);
          a union c will result to 1.5 and 1923
        */
        has_real_type = true;
      } else {
        /*
          create table sb(a int(3));//3 ? display width ? precision? length?
          create table sc(c decimal(5,3));
          insert sb values (12345);
          insert sc values(123.33);
          a union c will result to 12345 and 123.33
        */
        if (ob_is_integer_type(types[i].get_type())) {
          precision = MAX(ObAccuracy::DDL_DEFAULT_ACCURACY2[is_oracle_mode][types[i].get_type()].precision_,
              types[i].get_precision());
          scale = 0;
        } else if (ob_is_number_tc(types[i].get_type()) || ob_is_temporal_type(types[i].get_type())) {
          precision = types[i].get_precision();
          scale = types[i].get_scale();
        } else {
          // string type. precision and scale means nothing to string types
          // just let it go
        }

        if (precision >= 0 && precision - scale > max_integer_digits) {
          max_integer_digits = static_cast<int16_t>(precision - scale);
        }
        if (scale >= 0 && scale > max_decimal_digits) {
          max_decimal_digits = scale;
        }
      }
    }

    if (OB_FAIL(ret)) {
    } else if (ob_is_real_type(type.get_type()) && has_real_type) {
      type.set_precision(PRECISION_UNKNOWN_YET);
      type.set_scale(SCALE_UNKNOWN_YET);
    } else {
      if (max_integer_digits + max_decimal_digits >= 0) {
        precision = static_cast<ObPrecision>(max_integer_digits + max_decimal_digits);
        type.set_precision(MIN(precision, ObAccuracy::MAX_ACCURACY2[is_oracle_mode][type.get_type()].precision_));
      }
      if (max_decimal_digits >= 0) {
        type.set_scale(MIN(max_decimal_digits, ObAccuracy::MAX_ACCURACY2[is_oracle_mode][type.get_type()].scale_));
      }
    }
  }
  return ret;
}

ObObjType ObExprOperator::enumset_calc_types_[ObMaxTC] = {
    ObUInt64Type,  /*ObNullTC*/
    ObUInt64Type,  /*ObIntTC*/
    ObUInt64Type,  /*ObUIntTC*/
    ObUInt64Type,  /*ObFloatTC*/
    ObUInt64Type,  /*ObDoubleTC*/
    ObUInt64Type,  /*ObNumberTC*/
    ObVarcharType, /*ObDateTimeTC*/
    ObVarcharType, /*ObDateTC*/
    ObVarcharType, /*ObTimeTC*/
    ObUInt64Type,  /*ObYearTC*/
    ObVarcharType, /*ObStringTC*/
    ObMaxType,     /*ObExtendTC*/
    ObMaxType,     /*ObUnknownTC*/
    ObVarcharType, /*ObTextTC*/
    ObUInt64Type,  /*ObBitTC*/
    ObVarcharType, /*ObEnumSetTC*/
    ObVarcharType, /*ObEnumSetInnerTC*/
};
////////////////////////////////////////////////////////////////

int ObArithExprOperator::assign(const ObExprOperator& other)
{
  int ret = OB_SUCCESS;
  const ObArithExprOperator* tmp_other = dynamic_cast<const ObArithExprOperator*>(&other);
  if (OB_UNLIKELY(NULL == tmp_other)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument. wrong type for other", K(ret), K(other));
  } else if (this != tmp_other) {
    if (OB_FAIL(ObExprOperator::assign(other))) {
      LOG_WARN("copy in Base class ObExprOperator failed", K(ret));
    } else {
      this->result_type_func_ = tmp_other->result_type_func_;
      this->calc_type_func_ = tmp_other->calc_type_func_;
    }
  }
  return ret;
}

int ObArithExprOperator::calc_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  if (OB_ISNULL(result_type_func_) || OB_ISNULL(calc_type_func_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("result_type_func_  or calc_type_func_is null", K(ret));
  } else {
    ObObjType calc_type = ObMaxType;
    ObObjType calc_ob1_type = ObMaxType;
    ObObjType calc_ob2_type = ObMaxType;
    if (OB_UNLIKELY(NOT_ROW_DIMENSION != row_dimension_)) {
      ret = OB_ERR_INVALID_TYPE_FOR_OP;
    } else if (OB_FAIL(result_type_func_(type, type1, type2))) {
      LOG_WARN("fail to result_type_func_", K(ret));
    } else if (OB_UNLIKELY(ObMaxType == type.get_type())) {
      ret = OB_ERR_INVALID_TYPE_FOR_OP;
    } else if (OB_FAIL(calc_type_func_(calc_type, calc_ob1_type, calc_ob2_type, type1.get_type(), type2.get_type()))) {
      LOG_WARN("fail to calc_type_func_", K(ret));
    } else {
      type.set_calc_type(calc_type);
      type1.set_calc_type(get_calc_cast_type(type1.get_type(), calc_ob1_type));
      type2.set_calc_type(get_calc_cast_type(type2.get_type(), calc_ob2_type));
      ObExprOperator::calc_result_flag2(type, type1, type2);
    }

    LOG_DEBUG("arithmatic result type",
        K(type),
        K(type1),
        K(type2),
        K(calc_type),
        K(calc_ob1_type),
        K(calc_ob2_type),
        K(ret));
  }
  return ret;
}

int ObArithExprOperator::calc_result2(ObObj& result, const ObObj& left, const ObObj& right, ObExprCtx& expr_ctx) const
{
  return calc_(
      result, left, right, expr_ctx, result_type_.get_calc_scale(), result_type_.get_calc_type(), arith_funcs_);
}

int ObArithExprOperator::calc(ObObj& result, const ObObj& left, const ObObj& right, ObIAllocator* allocator,
    ObScale scale, ObCalcTypeFunc calc_type_func, const ObArithFunc* arith_funcs)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(calc_type_func) || OB_ISNULL(allocator) || OB_ISNULL(arith_funcs)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("the pointer is null", K(calc_type_func), K(allocator), K(arith_funcs), K(ret));
  } else {
    ObObjType calc_type = ObMaxType;
    ObObjType calc_ob1_type = ObMaxType;
    ObObjType calc_ob2_type = ObMaxType;
    if (OB_FAIL(calc_type_func(calc_type, calc_ob1_type, calc_ob2_type, left.get_type(), right.get_type()))) {
    } else {
      ObExprCtx expr_ctx(NULL, NULL, NULL, allocator);
      ret = calc_(result, left, right, expr_ctx, scale, calc_type, arith_funcs);
    }
  }
  return ret;
}

int ObArithExprOperator::calc(ObObj& result, const ObObj& left, const ObObj& right, ObExprCtx& expr_ctx,
    ObScale calc_scale, ObCalcTypeFunc calc_type_func, const ObArithFunc* arith_funcs)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(calc_type_func) || OB_ISNULL(arith_funcs)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("calc_type_func is null", K(ret));
  } else {
    ObObjType calc_type = ObMaxType;
    ObObjType calc_ob1_type = ObMaxType;
    ObObjType calc_ob2_type = ObMaxType;
    if (OB_FAIL(calc_type_func(calc_type, calc_ob1_type, calc_ob2_type, left.get_type(), right.get_type()))) {
    } else {
      ret = calc_(result, left, right, expr_ctx, calc_scale, calc_type, arith_funcs);
    }
  }
  return ret;
}

int ObArithExprOperator::calc_(ObObj& result, const ObObj& left, const ObObj& right, ObExprCtx& expr_ctx,
    ObScale calc_scale, ObObjType calc_type, const ObArithFunc* arith_funcs)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(arith_funcs) || OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("the pointer is null", K(arith_funcs), K(expr_ctx.calc_buf_), K(ret));
  } else if (OB_UNLIKELY(ObNullType == calc_type || ObMaxType == calc_type)) {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
    LOG_WARN("result type is invalid", K(ret), K(calc_type));
  }

  if (OB_SUCC(ret) && (OB_UNLIKELY(ObNullType == left.get_type() || ObNullType == right.get_type()))) {
    result.set_null();
  } else if (OB_SUCC(ret)) {
    const ObObj* res_left = &left;
    const ObObj* res_right = &right;
    ObObj tmp_left_obj;
    ObObj tmp_right_obj;
    if (left.get_type() != calc_type || right.get_type() != calc_type) {
      EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NO_CAST_INT_UINT);
      if (is_datetime_add_minus_calc(calc_type, arith_funcs)) {
        if (OB_FAIL(ObObjCaster::to_datetime(calc_type, cast_ctx, left, tmp_left_obj, res_left))) {
          LOG_WARN("cast failed.", K(ret), K(left), K(calc_type));
        } else if (OB_FAIL(ObObjCaster::to_datetime(calc_type, cast_ctx, right, tmp_right_obj, res_right))) {
          LOG_WARN("cast failed.", K(ret), K(right), K(calc_type));
        }
      } else if (OB_FAIL(ObObjCaster::to_type(calc_type, cast_ctx, left, tmp_left_obj, res_left))) {
        LOG_WARN("cast failed.", K(ret), K(left), K(calc_type));
      } else if (OB_FAIL(ObObjCaster::to_type(calc_type, cast_ctx, right, tmp_right_obj, res_right))) {
        LOG_WARN("cast failed.", K(ret), K(right), K(calc_type));
      }
    }
    // ok, let's calc it.
    if (OB_SUCC(ret)) {
      ObArithFunc arith_func = arith_funcs[res_left->get_type_class()];
      LOG_DEBUG("begin calc", K(left), KPC(res_left), K(right), KPC(res_right), K(calc_scale), K(calc_type), K(lbt()));
      if (OB_ISNULL(arith_func)) {
        ret = OB_INVALID_ARGUMENT;
        LOG_WARN("arith_func is null", K(ret));
      } else {
        ret = arith_func(result, *res_left, *res_right, expr_ctx.calc_buf_, calc_scale);
        if (OB_ERR_UNEXPECTED == ret) {
          ob_abort();
        }
      }
    }
    //    }
  }
  return ret;
}

bool ObArithExprOperator::is_datetime_add_minus_calc(const common::ObObjType calc_type, const ObArithFunc* arith_funcs)
{
  return (NULL != arith_funcs && (ObExprAdd::add_datetime == arith_funcs[ob_obj_type_class(calc_type)] ||
                                     ObExprMinus::minus_datetime == arith_funcs[ob_obj_type_class(calc_type)]));
}

int ObRelationalExprOperator::deserialize(const char* buf, const int64_t data_len, int64_t& pos)
{
  int ret = OB_SUCCESS;
  cmp_op_func2_ = NULL;
  ret = ObExprOperator::deserialize(buf, data_len, pos);
  if (OB_SUCC(ret) && (IS_COMMON_COMPARISON_OP(type_) || T_FUN_SYS_STRCMP == type_) &&
      ObExprOperator::NOT_ROW_DIMENSION == row_dimension_ && real_param_num_ == 2) {
    if (input_types_.count() != 2) {
      ret = OB_ERR_UNEXPECTED;
      LOG_ERROR("unexpected error. invalid input types", K(ret), K(type_), K(real_param_num_), K(input_types_.count()));
    } else {
      ObObjType left_operand_type = input_types_.at(0).get_calc_type();
      ObObjType right_operand_type = input_types_.at(1).get_calc_type();
      if (OB_FAIL(set_cmp_func(left_operand_type, right_operand_type))) {
        LOG_WARN("set cmp func failed", K(ret), K(left_operand_type), K(right_operand_type), K(type_));
        cmp_op_func2_ = NULL;  // defensive code
      }
    }
  }
  return ret;
}

int ObRelationalExprOperator::compare(ObObj& result, const ObObj& obj1, const ObObj& obj2, const ObCompareCtx& cmp_ctx,
    ObCastCtx& cast_ctx, ObCmpOp cmp_op)
{
  int ret = OB_SUCCESS;
  bool need_cast = false;
  if (OB_FAIL(compare_nocast(result, obj1, obj2, cmp_ctx, cmp_op, need_cast))) {
    LOG_WARN("failed to compare objects", K(obj1), K(obj2));
  } else if (need_cast) {
    if (OB_FAIL(compare_cast(result, obj1, obj2, cmp_ctx, cast_ctx, cmp_op))) {
      LOG_WARN("failed to compare objects", K(obj1), K(obj2));
    }
  }
  return ret;
}

int ObRelationalExprOperator::compare_cast(ObObj& result, const ObObj& obj1, const ObObj& obj2,
    const ObCompareCtx& cmp_ctx, ObCastCtx& cast_ctx, ObCmpOp cmp_op)
{
  int ret = OB_SUCCESS;
  ObObj buf_obj1;
  ObObj buf_obj2;
  const ObObj* res_obj1 = NULL;
  const ObObj* res_obj2 = NULL;
  bool need_cast = false;
  cast_ctx.dest_collation_ = cmp_ctx.cmp_cs_type_;
  if (OB_FAIL(ObObjCaster::to_type(cmp_ctx.cmp_type_, cast_ctx, obj1, buf_obj1, res_obj1))) {
    LOG_WARN("failed to cast obj", K(ret), K(cmp_ctx.cmp_type_), K(obj1));
  } else if (OB_FAIL(ObObjCaster::to_type(cmp_ctx.cmp_type_, cast_ctx, obj2, buf_obj2, res_obj2))) {
    LOG_WARN("failed to cast obj", K(ret), K(cmp_ctx.cmp_type_), K(obj2));
  } else if (OB_ISNULL(res_obj1) || OB_ISNULL(res_obj2)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("res_obj1 or res_obj2 is null");
  } else if (OB_FAIL(ObObjCmpFuncs::compare(result, *res_obj1, *res_obj2, cmp_ctx, cmp_op, need_cast))) {
    LOG_WARN("failed to compare objects", K(ret), K(*res_obj1), K(*res_obj2), K(cmp_op));
  } else if (need_cast) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("failed to compare objects", K(ret), K(*res_obj1), K(*res_obj2), K(cmp_op));
  }
  return ret;
}

int ObRelationalExprOperator::compare_nullsafe(int64_t& result, const ObObj& obj1, const ObObj& obj2,
    ObCastCtx& cast_ctx, ObObjType cmp_type, ObCollationType cmp_cs_type)
{
  int ret = OB_SUCCESS;
  ObObj res_obj;
  int64_t tz_offset = 0;
  if (OB_FAIL(get_tz_offset(cast_ctx.dtc_params_.tz_info_, tz_offset))) {
    LOG_WARN("get tz_offset failed", K(ret), K(cast_ctx.dtc_params_.tz_info_), K(tz_offset));
  } else {
    ObCompareCtx cmp_ctx(cmp_type, cmp_cs_type, true, tz_offset, default_null_pos());
    if (OB_FAIL(compare(res_obj, obj1, obj2, cmp_ctx, cast_ctx, CO_CMP))) {
    } else {
      result = res_obj.get_int();
    }
  }
  return ret;
}

bool ObRelationalExprOperator::can_cmp_without_cast(
    const ObExprResType& type1, const ObExprResType& type2, ObCmpOp cmp_op, const ObSQLSessionInfo& session)
{
  bool need_no_cast = false;
  if (ob_is_enum_or_set_type(type1.get_type()) && ob_is_enum_or_set_type(type2.get_type())) {
    need_no_cast = false;
  } else if (session.use_static_typing_engine()) {
    if (ObDatumFuncs::is_string_type(type1.get_type()) && ObDatumFuncs::is_string_type(type2.get_type())) {
      need_no_cast = ObCharset::charset_type_by_coll(type1.get_collation_type()) ==
                     ObCharset::charset_type_by_coll(type2.get_collation_type());
    } else {
      auto func_ptr = ObExprCmpFuncsHelper::get_eval_expr_cmp_func(
          type1.get_type(), type2.get_type(), cmp_op, lib::is_oracle_mode(), CS_TYPE_BINARY);
      need_no_cast = (func_ptr != nullptr);
    }
  } else {
    obj_cmp_func cmp_func = NULL;
    need_no_cast = ObObjCmpFuncs::can_cmp_without_cast(type1, type2, cmp_op, cmp_func);
  }
  return need_no_cast;
}

/*
 * Note that, the original motivation of this function is that,
 *
 * Provided that we have a = b and b = c in which a , b and c
 * are of metainfo meta1, meta2, meta3, respectively
 *
 * So, can we deduce that a = c holds actually?
 *
 * this func tells you this via result when and only when the ret is OB_SUCCESS
 */

int ObRelationalExprOperator::is_equivalent(
    const ObObjMeta& meta1, const ObObjMeta& meta2, const ObObjMeta& meta3, bool& result)
{
  int ret = OB_SUCCESS;
  result = false;
  ObObjMeta equal_meta12;
  ObObjMeta equal_meta23;
  ObObjMeta equal_meta13;
  if (OB_FAIL(get_equal_meta(equal_meta12, meta1, meta2))) {
    LOG_WARN("get equal meta failed", K(ret), K(meta1), K(meta2));
  } else if (OB_FAIL(get_equal_meta(equal_meta13, meta1, meta3))) {
    LOG_WARN("get equal meta failed", K(ret), K(meta1), K(meta3));
  } else if (OB_FAIL(get_equal_meta(equal_meta23, meta2, meta3))) {
    LOG_WARN("get equal meta failed", K(ret), K(meta2), K(meta3));
  } else if (OB_UNLIKELY(equal_meta12.get_type() == ObMaxType ||
                         equal_meta13.get_type() == ObMaxType /* no need to check ObNullType here*/
                         || equal_meta23.get_type() == ObMaxType)) {
    /*result = false;*/
  } else if (equal_meta12.get_type() == equal_meta13.get_type() && equal_meta13.get_type() == equal_meta23.get_type()) {
    if (OB_UNLIKELY(ob_is_string_or_lob_type(equal_meta12.get_type()))) {
      // all are string type
      result = equal_meta12.get_collation_type() == equal_meta13.get_collation_type() &&
               equal_meta13.get_collation_type() == equal_meta23.get_collation_type();
    } else {
      result = true;
    }
  }
  return ret;
}

int ObRelationalExprOperator::get_equal_meta(ObObjMeta& meta, const ObObjMeta& meta1, const ObObjMeta& meta2)
{
  int ret = OB_SUCCESS;
  ObObjType type = ObMaxType;
  if (OB_FAIL(ObExprResultTypeUtil::get_relational_equal_type(type, meta1.get_type(), meta2.get_type()))) {
    LOG_WARN("get equal type failed", K(ret), K(meta1), K(meta2));
  } else if (ob_is_string_or_lob_type(type)) {
    ObObjMeta coll_types[2] = {meta1, meta2};
    ret = aggregate_charsets_for_comparison(meta, coll_types, 2, CS_TYPE_INVALID);
  }
  if (OB_SUCC(ret)) {
    meta.set_type(type);
  }
  return ret;
}

// MySQL use item_cmp_type to calc target compare type
int ObExprOperator::calc_cmp_type2(
    ObExprResType& type, const ObExprResType& type1, const ObExprResType& type2, const ObCollationType coll_type) const
{
  int ret = OB_SUCCESS;
  ObObjType cmp_type = ObMaxType;
  // todo: we can add such code after plan cache add same optimization.
  //  if (is_int_cmp_const_str(&type1, &type2, cmp_type)) {
  //  } else
  if (is_oracle_mode() && (type1.is_blob() || type2.is_blob() || type1.is_blob_locator() || type2.is_blob_locator()) &&
      type_ != T_OP_IS && type_ != T_OP_IS_NOT) {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
    LOG_USER_ERROR(OB_ERR_INVALID_TYPE_FOR_OP, ob_obj_type_str(type1.get_type()), ob_obj_type_str(type2.get_type()));
  } else if (OB_FAIL(ObExprResultTypeUtil::get_relational_cmp_type(cmp_type, type1.get_type(), type2.get_type()))) {
    LOG_WARN("fail to get cmp_type", K(ret));
  } else if (OB_UNLIKELY(ObMaxType == cmp_type)) {
    ret = OB_INVALID_ARGUMENT;
  } else if (ObURowIDType == cmp_type) {
    // only support <urwoid, urowid>,<string, urwoid>, <urowid, string>,
    // <NULL, urowid>, <urowid, NULL>
    if (OB_UNLIKELY(ObStringTC != type1.get_type_class() && ObRowIDTC != type1.get_type_class() &&
                    ObNullTC != type1.get_type_class()) ||
        OB_UNLIKELY(ObStringTC != type2.get_type_class() && ObRowIDTC != type2.get_type_class() &&
                    ObNullTC != type2.get_type_class())) {
      ret = OB_INVALID_ROWID;
      LOG_WARN("not supported compare type", K(type1), K(type2));
    }
  }
  if (OB_SUCC(ret)) {
    type.set_calc_type(cmp_type);
    // collation
    if (ob_is_string_or_lob_type(cmp_type)) {
      ObObjMeta coll_types[2];
      coll_types[0].set_collation(type1);
      coll_types[1].set_collation(type2);
      ret = aggregate_charsets_for_comparison(type.get_calc_meta(), coll_types, 2, coll_type);
      if (OB_FAIL(ret)) {
        LOG_WARN("aggregate charset failed", K(type1), K(type2), K(type.get_calc_meta()));
      }
    } else if (ObRawType == cmp_type) {
      type.get_calc_meta().set_collation_type(CS_TYPE_BINARY);
    }
    LOG_DEBUG("calc cmp type", K(type1), K(type2), K(type.get_calc_meta()));
  }
  return ret;
}

int ObExprOperator::calc_cmp_type3(ObExprResType& type, const ObExprResType& type1, const ObExprResType& type2,
    const ObExprResType& type3, const ObCollationType coll_type) const
{
  int ret = OB_SUCCESS;
  // cmp type
  ObObjType cmp_type = type1.get_type();
  if (is_oracle_mode() && (type1.is_blob() || type1.is_blob_locator() || type2.is_blob() || type2.is_blob_locator() ||
                              type3.is_blob() || type3.is_blob_locator())) {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
    LOG_USER_ERROR(OB_ERR_INVALID_TYPE_FOR_OP, ob_obj_type_str(type1.get_type()), ob_obj_type_str(type2.get_type()));
  } else if (OB_SUCC(ObExprResultTypeUtil::get_relational_cmp_type(cmp_type, type2.get_type(), cmp_type))) {
    if (OB_SUCC(ObExprResultTypeUtil::get_relational_cmp_type(cmp_type, cmp_type, type3.get_type()))) {
      if (OB_UNLIKELY(ObMaxType == cmp_type)) {
        ret = OB_INVALID_ARGUMENT;  // not compatible input
      }
    }
  }

  // collation
  if (OB_SUCC(ret)) {
    type.set_calc_type(cmp_type);
    if (ob_is_string_or_lob_type(cmp_type)) {
      ObObjMeta coll_types[3];
      coll_types[0].set_collation(type1);
      coll_types[1].set_collation(type2);
      coll_types[2].set_collation(type3);
      ret = aggregate_charsets_for_comparison(type.get_calc_meta(), coll_types, 3, coll_type);
    } else if (ObRawType == cmp_type) {
      type.get_calc_meta().set_collation_type(CS_TYPE_BINARY);
    }
  }
  return ret;
}

// for sqrt, ln, e functions
int ObExprOperator::calc_trig_function_result_type1(
    ObExprResType& type, ObExprResType& type1, common::ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  if (NOT_ROW_DIMENSION != row_dimension_ || ObMaxType == type1.get_type()) {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  } else if (!share::is_oracle_mode()) {
    type.set_double();
  } else {
    if (ob_is_real_type(type1.get_type())) {
      type.set_double();
    } else {
      type.set_number();
    }
    type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY2[ORACLE_MODE][type.get_type()].get_scale());
    type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY2[ORACLE_MODE][type.get_type()].get_precision());
  }
  type1.set_calc_type(type.get_type());
  //no need add not null check for trig/ln/e funciotn in mysql mode
  return ret;
}

int ObExprOperator::calc_trig_function_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, common::ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  if (NOT_ROW_DIMENSION != row_dimension_) {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;  // arithmetic not support row
  } else if (ObMaxType == type1.get_type() || ObMaxType == type2.get_type()) {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  } else if (!share::is_oracle_mode()) {
    type.set_double();
  } else {
    if (ob_is_real_type(type1.get_type()) || ob_is_real_type(type2.get_type())) {
      type.set_double();
    } else {
      type.set_number();
    }
    type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY2[ORACLE_MODE][type.get_type()].get_scale());
    type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY2[ORACLE_MODE][type.get_type()].get_precision());
  }
  type1.set_calc_type(type.get_type());
  type2.set_calc_type(type.get_type());
  ObExprOperator::calc_result_flag2(type, type1, type2);
  return ret;
}

ObCastMode ObExprOperator::get_cast_mode() const
{
  return CM_NONE;
}

ObExpr* ObExprOperator::get_rt_expr(const ObRawExpr& raw_expr) const
{
  return raw_expr.rt_expr_;
}

OB_SERIALIZE_MEMBER(ObIterExprOperator, expr_id_, expr_type_);

int ObRelationalExprOperator::calc_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  return deduce_cmp_type(*this, type, type1, type2, type_ctx);
}

int ObRelationalExprOperator::deduce_cmp_type(const ObExprOperator& expr, ObExprResType& type, ObExprResType& type1,
    ObExprResType& type2, ObExprTypeCtx& type_ctx)
{
  int ret = OB_SUCCESS;
  ObExprResType cmp_type;
  CK(NULL != type_ctx.get_session());
  if (OB_FAIL(ret)) {
  } else if (OB_SUCC(expr.calc_cmp_type2(cmp_type, type1, type2, type_ctx.get_coll_type()))) {
    type.set_int32();  // not tinyint, compatible with MySQL
    type.set_precision(DEFAULT_PRECISION_FOR_BOOL);
    type.set_scale(DEFAULT_SCALE_FOR_INTEGER);
    type.set_calc_collation(cmp_type);
    type.set_calc_type(cmp_type.get_calc_type());
    ObExprOperator::calc_result_flag2(type, type1, type2);
    bool need_no_cast = can_cmp_without_cast(type1, type2, get_cmp_op(expr.get_type()), *type_ctx.get_session());
    type1.set_calc_type(need_no_cast ? type1.get_type() : cmp_type.get_calc_type());
    type2.set_calc_type(need_no_cast ? type2.get_type() : cmp_type.get_calc_type());
    if (ob_is_string_or_lob_type(cmp_type.get_calc_type())) {
      type1.set_calc_collation_type(cmp_type.get_calc_collation_type());
      type2.set_calc_collation_type(cmp_type.get_calc_collation_type());
    } else if (ObRawType == cmp_type.get_calc_type()) {
      type1.set_calc_collation_type(CS_TYPE_BINARY);
      type2.set_calc_collation_type(CS_TYPE_BINARY);
    }
  }
  return ret;
}

// TODO():invoke  type has not set calc_type
int ObRelationalExprOperator::calc_result_type3(ObExprResType& type, ObExprResType& type1, ObExprResType& type2,
    ObExprResType& type3, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  ObExprResType cmp_type;
  if (OB_SUCC(calc_cmp_type3(cmp_type, type1, type2, type3, type_ctx.get_coll_type()))) {
    type.set_int32();
    type.set_calc_collation(cmp_type);
    type.set_calc_type(cmp_type.get_calc_type());
    type.set_scale(DEFAULT_SCALE_FOR_INTEGER);
    type.set_precision(DEFAULT_PRECISION_FOR_BOOL);
    ObExprOperator::calc_result_flag3(type, type1, type2, type3);
    if (OB_FAIL(calc_calc_type3(type1, type2, type3, type_ctx, cmp_type.get_calc_type()))) {
      LOG_WARN("set calc type failed", K(type1), K(type2), K(type3), K(cmp_type), K(ret));
    }
    if (ob_is_string_or_lob_type(cmp_type.get_calc_type())) {
      type1.set_calc_collation_type(cmp_type.get_calc_collation_type());
      type2.set_calc_collation_type(cmp_type.get_calc_collation_type());
      type3.set_calc_collation_type(cmp_type.get_calc_collation_type());
    } else if (ObRawType == cmp_type.get_calc_type()) {
      type1.set_calc_collation_type(CS_TYPE_BINARY);
      type2.set_calc_collation_type(CS_TYPE_BINARY);
      type3.set_calc_collation_type(CS_TYPE_BINARY);
    }
  }
  return ret;
}

int ObRelationalExprOperator::calc_result_typeN(
    ObExprResType& type, ObExprResType* types, int64_t param_num, ObExprTypeCtx& type_ctx) const
{

  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(0 >= row_dimension_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or row_dimension is less than 0", K(types), K(row_dimension_), K(ret));
  } else if (OB_UNLIKELY(param_num <= 0 || 2 != param_num / row_dimension_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("param_num is wrong", K(param_num), K(ret));
  } else if (OB_FAIL(type.init_row_dimension(row_dimension_))) {
    LOG_WARN("fail to init row dimemnsion", K(ret));
  } else {
    // () MySQL deal with (a,b) = (c, d) policy:
    // compare pair by pair, with different types
    // select (1, '1.000000') = ('1.00x', '1.000000');
    // 1 compare with '1.00x' as int,
    // '1.00000' compare with '1.000000' as string
    ObExprResType tmp_res_type;
    for (int64_t i = 0; OB_SUCC(ret) && i < row_dimension_; ++i) {
      if (OB_FAIL(ObRelationalExprOperator::calc_result_type2(
              tmp_res_type, types[i], types[i + row_dimension_], type_ctx))) {
        LOG_WARN("failed to calc result types", K(ret));
      } else if (OB_FAIL(type.get_row_calc_cmp_types().push_back(tmp_res_type.get_calc_meta()))) {
        LOG_WARN("failed to push back cmp type", K(ret));
      } else {
        if (ob_is_string_or_lob_type(tmp_res_type.get_calc_type())) {
          types[i].set_calc_collation_type(tmp_res_type.get_calc_collation_type());
          types[i + row_dimension_].set_calc_collation_type(tmp_res_type.get_calc_collation_type());
        } else if (ObRawType == tmp_res_type.get_calc_type()) {
          types[i].set_calc_collation_type(CS_TYPE_BINARY);
          types[i + row_dimension_].set_calc_collation_type(CS_TYPE_BINARY);
        }
      }
    }
    // overall result type
    if (OB_SUCC(ret)) {
      type.set_int32();
      type.set_precision(DEFAULT_PRECISION_FOR_BOOL);
      type.set_scale(DEFAULT_SCALE_FOR_INTEGER);
      ObExprOperator::calc_result_flagN(type, types, param_num);
    }
  }
  return ret;
}

int ObRelationalExprOperator::calc_calc_type3(ObExprResType& type1, ObExprResType& type2, ObExprResType& type3,
    ObExprTypeCtx& type_ctx, const ObObjType cmp_type) const
{
  int ret = OB_SUCCESS;
  // a(type1) between b(type2) and c(type3) <==> b<=a && a <=c
  ObExprResType cmp_type21;  // b <= a
  ObExprResType cmp_type13;  // a <= c
  bool need_no_cast = false;
  CK(NULL != type_ctx.get_session());
  if (OB_FAIL(ret)) {
  } else if (OB_FAIL(calc_cmp_type2(cmp_type21, type2, type1, type_ctx.get_coll_type()))) {
    LOG_WARN("get cmp failed", K(ret), K(type2), K(type1));
  } else if (OB_FAIL(calc_cmp_type2(cmp_type13, type1, type3, type_ctx.get_coll_type()))) {
    LOG_WARN("get cmp failed", K(ret), K(type1), K(type3));
  } else {
    ObObjType type21 = cmp_type21.get_type();
    ObObjType type13 = cmp_type13.get_type();
    if (OB_LIKELY(type21 == type13 && type13 == cmp_type)) {
      // type21 == type13 == cmp_type
      bool need_no_cast_21 = can_cmp_without_cast(type2, type1, get_cmp_op(type_), *type_ctx.get_session());
      bool need_no_cast_13 = can_cmp_without_cast(type1, type3, get_cmp_op(type_), *type_ctx.get_session());
      need_no_cast = need_no_cast_21 && need_no_cast_13;
    }
  }
  if (OB_SUCC(ret)) {
    if (need_no_cast) {
      type1.set_calc_type(type1.get_type());
      type2.set_calc_type(type2.get_type());
      type3.set_calc_type(type3.get_type());
    } else {
      type1.set_calc_type(cmp_type);
      type2.set_calc_type(cmp_type);
      type3.set_calc_type(cmp_type);
    }
  }
  return ret;
}

int ObRelationalExprOperator::get_cmp_result_type3(ObExprResType& type, bool& need_no_cast, const ObExprResType* types,
    const int64_t param_num, const sql::ObSQLSessionInfo& my_session)
{
  int ret = OB_SUCCESS;
  need_no_cast = false;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1) || OB_UNLIKELY(param_num > 3)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else if (1 == param_num) {
    // this is for case when clause like case  when 1 then c1 end;
    type = types[0];
    need_no_cast = true;
  } else {
    ObCollationType coll_type = CS_TYPE_INVALID;
    const ObLengthSemantics default_ls = my_session.get_actual_nls_length_semantics();
    if (OB_FAIL(my_session.get_collation_connection(coll_type))) {
      LOG_WARN("fail to get_collation_connection", K(ret));
    } else if (2 == param_num) {
      need_no_cast = can_cmp_without_cast(types[1], types[0], CO_CMP, my_session);
      if (!need_no_cast) {
        ret = calc_cmp_type2(type, types[0], types[1], coll_type);
      }
    } else {
      const ObExprResType& type1 = types[0];
      const ObExprResType& type2 = types[1];
      const ObExprResType& type3 = types[2];
      // a(type1) between b(type2) and c(type3) <==> b<=a && a <=c
      ObExprResType cmp_type21;  // b <= a
      ObExprResType cmp_type13;  // a <= c
      if (OB_FAIL(calc_cmp_type3(type, type1, type2, type3, coll_type))) {
        LOG_WARN("fail to calc_cmp_type3", K(ret));
      } else if (OB_FAIL(calc_cmp_type2(cmp_type21, type2, type1, coll_type))) {
        LOG_WARN("get cmp failed", K(ret), K(type2), K(type1));
      } else if (OB_FAIL(calc_cmp_type2(cmp_type13, type1, type3, coll_type))) {
        LOG_WARN("get cmp failed", K(ret), K(type1), K(type3));
      } else if (cmp_type21.get_type() == cmp_type13.get_type() && cmp_type13.get_type() == type.get_calc_type()) {
        // type21 == type13 == cmp_type
        bool need_no_cast_21 = can_cmp_without_cast(type2, type1, CO_CMP, my_session);
        bool need_no_cast_13 = can_cmp_without_cast(type1, type3, CO_CMP, my_session);
        need_no_cast = need_no_cast_21 && need_no_cast_13;
      }
    }
  }
  return ret;
}

int ObRelationalExprOperator::calc_result2(
    ObObj& result, const ObObj& obj1, const ObObj& obj2, ObExprCtx& expr_ctx, bool is_null_safe, ObCmpOp cmp_op) const
{
  int ret = OB_SUCCESS;
  // TODO:: raw
  //  bool need_cast = (share::is_oracle_mode() && obj1.get_collation_type() != obj2.get_collation_type());
  bool need_cast = (share::is_oracle_mode() && obj1.get_type() != obj2.get_type());
  EXPR_DEFINE_CMP_CTX(result_type_.get_calc_meta(), is_null_safe, expr_ctx);
  /*
   * FIX ME,please. It seems that we must check obj1 and obj2 are null or not here
   *
   * But this is so ugly and everything will be retarded
   */
  if (OB_LIKELY(!obj1.is_null() && !obj2.is_null() && cmp_op_func2_ != NULL && !need_cast)) {
    if (OB_FAIL(compare_nocast(result, obj1, obj2, cmp_ctx, cmp_op, cmp_op_func2_))) {
      LOG_WARN("failed to compare objects", K(ret), K(obj1), K(obj2));
    }
  } else if (!need_cast && OB_FAIL(compare_nocast(result, obj1, obj2, cmp_ctx, cmp_op, need_cast))) {
    LOG_WARN("failed to compare objects", K(ret), K(obj1), K(obj2));
  } else if (need_cast) {
    EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
    if (OB_FAIL(compare_cast(result, obj1, obj2, cmp_ctx, cast_ctx, cmp_op))) {
      LOG_WARN("failed to compare objects", K(ret), K(obj1), K(obj2));
    }
  }
  return ret;
}

/*If you know little about vector compare in mysql, before you start to read this function,
 *it is highly recommended for you to digg into this:
 *http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html
 *
 */
int ObRelationalExprOperator::calc_resultN(ObObj& result, const ObObj* objs_array, int64_t param_num,
    ObExprCtx& expr_ctx, bool is_null_safe, ObCmpOp cmp_op) const
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(0 >= row_dimension_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("row_dimension_ is less or equal to 0", K(ret));
  } else if (OB_ISNULL(objs_array) || OB_UNLIKELY(0 >= param_num) || OB_UNLIKELY(0 != param_num % row_dimension_) ||
             OB_UNLIKELY(2 != param_num / row_dimension_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("objs_array or param_num or row_dimension_ is wrong", K(ret), K(param_num), K(row_dimension_));
  } else if (cmp_op < CO_EQ || cmp_op >= CO_CMP) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid cmp_op argument", K(ret), K(cmp_op));
  } else {
    EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
    const ObIArray<ObExprCalcType>& cmp_types = result_type_.get_row_calc_cmp_types();
    int64_t i = 0;
    bool row_cnt_null = false;
    ObCompareCtx cmp_ctx(ObMaxType, CS_TYPE_INVALID, is_null_safe, expr_ctx.tz_offset_, default_null_pos());
    // firstly, we try to locate the first non-equal pair
    for (i = 0; OB_SUCC(ret) && i < row_dimension_; ++i) {
      cmp_ctx.cmp_type_ = cmp_types.at(i).get_type();
      cmp_ctx.cmp_cs_type_ = cmp_types.at(i).get_collation_type();
      if (OB_FAIL(compare(result, objs_array[i], objs_array[i + row_dimension_], cmp_ctx, cast_ctx, CO_NE))) {
        LOG_WARN("compare failed", K(objs_array[i]), K(objs_array[i + row_dimension_]));
      } else if (result.is_null()) {
        row_cnt_null = true;
        // no break here
      } else if (result.is_true()) {
        break;
      }
    }  // end for
    if (OB_SUCC(ret)) {
      if (i == row_dimension_) {  // all pairs are null or equal
        if (row_cnt_null) {       // such as (1,2) op (null, 2) , (1,2) op (null, null)
          result.set_null();
        } else {  // all pairs are equal. such as (1,2) op (1,2)
          if (CO_EQ == cmp_op || CO_LE == cmp_op || CO_GE == cmp_op) {
            result.set_bool(true);
          } else {
            result.set_bool(false);
          }
        }
      } else {               // exist one pair whose comparison result is non-equal
        if (row_cnt_null) {  // such as (1,2) op (null, 3)
          if (CO_NE == cmp_op) {
            result.set_bool(true);
          } else if (CO_EQ == cmp_op) {
            result.set_bool(false);
          } else {
            result.set_null();
          }
        } else {  // such as (1,2,3) op (1,2,4)
          cmp_ctx.cmp_type_ = cmp_types.at(i).get_type();
          cmp_ctx.cmp_cs_type_ = cmp_types.at(i).get_collation_type();
          if (OB_FAIL(compare(result, objs_array[i], objs_array[i + row_dimension_], cmp_ctx, cast_ctx, cmp_op))) {
            LOG_WARN("compare failed", K(objs_array[i]), K(objs_array[i + row_dimension_]), K(cmp_op));
          }
        }
      }
    }
    if (OB_SUCC(ret)) {
      if (false == result.is_null()) {
        // compatiable with MySQL,relational op result type is int32
        result.set_int(ObInt32Type, result.get_int());
      }
    }
  }
  return ret;
}

bool ObRelationalExprOperator::is_int_cmp_const_str(
    const ObExprResType* type1, const ObExprResType* type2, ObObjType& cmp_type)
{
  UNUSED(type1);
  UNUSED(type2);
  UNUSED(cmp_type);
  return false;
}

int ObRelationalExprOperator::assign(const ObExprOperator& other)
{
  int ret = OB_SUCCESS;
  const ObRelationalExprOperator* tmp_other = dynamic_cast<const ObRelationalExprOperator*>(&other);
  if (OB_UNLIKELY(NULL == tmp_other)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument. wrong type for other", K(ret), K(other));
  } else if (OB_LIKELY(this != tmp_other)) {
    if (OB_FAIL(ObExprOperator::assign(other))) {
      LOG_WARN("copy in Base class ObExprOperator failed", K(ret));
    } else {
      this->cmp_op_func2_ = tmp_other->cmp_op_func2_;
    }
  }
  return ret;
}

int ObRelationalExprOperator::set_cmp_func(const ObObjType type1, const ObObjType type2)
{
  int ret = OB_SUCCESS;
  ObCmpOp cmp_op = get_cmp_op(type_);
  ObObjTypeClass tc1 = ob_obj_type_class(type1);
  ObObjTypeClass tc2 = ob_obj_type_class(type2);
  if (OB_FAIL(ObObjCmpFuncs::get_cmp_func(tc1, tc2, cmp_op, cmp_op_func2_))) {
    LOG_WARN("get cmp func failed", K(type1), K(type2), K(tc1), K(tc2), K(ret));
  }
  return ret;
}

OB_SERIALIZE_MEMBER((ObSubQueryRelationalExpr, ObExprOperator), subquery_key_, left_is_iter_, right_is_iter_);

int ObSubQueryRelationalExpr::assign(const ObExprOperator& other)
{
  int ret = OB_SUCCESS;
  const ObSubQueryRelationalExpr* tmp_other = dynamic_cast<const ObSubQueryRelationalExpr*>(&other);
  if (OB_UNLIKELY(NULL == tmp_other)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument. wrong type for other", K(ret), K(other));
  } else if (OB_LIKELY(this != tmp_other)) {
    if (OB_FAIL(ObExprOperator::assign(other))) {
      LOG_WARN("copy in Base class ObExprOperator failed", K(ret));
    } else {
      this->subquery_key_ = tmp_other->subquery_key_;
      this->left_is_iter_ = tmp_other->left_is_iter_;
      this->right_is_iter_ = tmp_other->right_is_iter_;
    }
  }
  return ret;
}

int ObSubQueryRelationalExpr::calc_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  ObExprResType tmp_res_type;
  CK(NULL != type_ctx.get_session());
  OZ(type.init_row_dimension(1));
  if (OB_SUCC(ret) && !type_ctx.get_session()->use_static_typing_engine()) {
    // static typing engine not enabled, keep the old behavior:
    // type1, type2 are unchanged
    if (OB_FAIL(calc_result_type2_(tmp_res_type, type1, type2, type_ctx))) {
      LOG_WARN("failed to calc_result_type2_", K(ret));
    } else if (OB_FAIL(type.get_row_calc_cmp_types().push_back(tmp_res_type.get_calc_meta()))) {
      LOG_WARN("failed to push back cmp_type", K(ret));
    } else {
      type.set_tinyint();
      type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY[ObTinyIntType].scale_);
      type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY[ObTinyIntType].precision_);
      ObExprOperator::calc_result_flag2(type, type1, type2);
    }
  }
  if (OB_SUCC(ret) && type_ctx.get_session()->use_static_typing_engine()) {
    // static typing engine enabled, same with ObRelationalExprOperator::calc_result_type
    OZ(ObRelationalExprOperator::deduce_cmp_type(*this, type, type1, type2, type_ctx));
    OZ(type.get_row_calc_cmp_types().push_back(type.get_calc_meta()));
  }
  return ret;
}

int ObSubQueryRelationalExpr::calc_result_typeN(
    ObExprResType& type, ObExprResType* types, int64_t param_num, ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  CK(NULL != type_ctx.get_session());
  if (OB_FAIL(ret)) {
  } else if (OB_ISNULL(types) || OB_UNLIKELY(0 >= row_dimension_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or row_dimension_ is less equal than 0", K(types), K(row_dimension_), K(ret));
  } else if (OB_UNLIKELY(param_num <= 0 || 2 != param_num / row_dimension_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("param_num is wrong", K(ret));
  } else if (OB_FAIL(type.init_row_dimension(row_dimension_))) {
    LOG_WARN("fail to init row dimension", K(ret));
  }

  if (OB_SUCC(ret) && !type_ctx.get_session()->use_static_typing_engine()) {
    // static typing engine not enabled, keep the old behavior unchanged:
    // parameters' type is unchanged.

    // select (c1, c2) = (select b1, b2 from B);
    // c1 compare with b1 with type X,
    // c2  compare with b2 with type Y
    ObExprResType tmp_res_type;
    for (int64_t i = 0; OB_SUCC(ret) && i < row_dimension_; ++i) {
      if (OB_FAIL(ObSubQueryRelationalExpr::calc_result_type2_(
              tmp_res_type, types[i], types[i + row_dimension_], type_ctx))) {
        LOG_WARN("fail to calc_result_type2_ when calc_result_typeN", K(ret));
      } else if (OB_FAIL(type.get_row_calc_cmp_types().push_back(tmp_res_type.get_calc_meta()))) {
        LOG_WARN("failed to push back cmp_type", K(ret));
      }
    }
    // overall result type
    if (OB_SUCC(ret)) {
      type.set_tinyint();
      ObExprOperator::calc_result_flagN(type, types, param_num);
    }
  }

  if (OB_SUCC(ret) && type_ctx.get_session()->use_static_typing_engine()) {
    for (int64_t i = 0; OB_SUCC(ret) && i < row_dimension_; i++) {
      ObExprResType tmp_res_type;
      OZ(ObRelationalExprOperator::deduce_cmp_type(*this, tmp_res_type, types[i], types[i + row_dimension_], type_ctx));
      OZ(type.get_row_calc_cmp_types().push_back(tmp_res_type.get_calc_meta()));
      if (OB_SUCC(ret)) {
        if (ob_is_string_type(tmp_res_type.get_calc_type())) {
          types[i].set_calc_collation_type(tmp_res_type.get_calc_collation_type());
          types[i + row_dimension_].set_calc_collation_type(tmp_res_type.get_calc_collation_type());
        } else if (ObRawType == tmp_res_type.get_calc_type()) {
          types[i].set_calc_collation_type(CS_TYPE_BINARY);
          types[i + row_dimension_].set_calc_collation_type(CS_TYPE_BINARY);
        }
      }
    }
    if (OB_SUCC(ret)) {
      type.set_int32();
      type.set_precision(DEFAULT_PRECISION_FOR_BOOL);
      type.set_scale(DEFAULT_SCALE_FOR_INTEGER);
      ObExprOperator::calc_result_flagN(type, types, param_num);
    }
  }

  return ret;
}
int ObSubQueryRelationalExpr::calc_result2(
    ObObj& result, const ObObj& obj1, const ObObj& obj2, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(!right_is_iter_) || OB_ISNULL(expr_ctx.subplan_iters_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("right_is_iter_ is wrong or expr_ctx.subplan_iters_ is null",
        K(right_is_iter_),
        K(expr_ctx.subplan_iters_),
        K(ret));
  } else {
    int64_t subquery_idx = OB_INVALID_INDEX;
    ObNewRow tmp_row;
    ObNewRow* left_row = NULL;
    ObNewRowIterator* left_row_iter = NULL;
    if (left_is_iter_) {
      int64_t left_idx = OB_INVALID_INDEX;
      if (OB_FAIL(obj1.get_int(left_idx))) {
        LOG_WARN("get left subquery index failed", K(ret), K(obj1));
      } else if (OB_ISNULL(left_row_iter = expr_ctx.subplan_iters_->at(left_idx))) {
        ret = OB_INVALID_ARGUMENT;
        LOG_WARN("get row iterator failed", K(left_idx));
      } else if (OB_FAIL(left_row_iter->get_next_row(left_row))) {
        if (OB_ITER_END == ret) {
          ret = OB_SUCCESS;
          result.set_null();
        } else {
          LOG_WARN("get next row from left row iterator failed", K(ret));
        }
      }
    } else {
      tmp_row.cells_ = const_cast<ObObj*>(&obj1);
      tmp_row.count_ = 1;
      left_row = &tmp_row;
    }
    if (OB_SUCC(ret) && NULL != left_row) {
      if (OB_FAIL(obj2.get_int(subquery_idx))) {
        LOG_WARN("get subquery index failed", K(ret), K(obj2));
      } else if (T_WITH_ALL == subquery_key_) {
        if (OB_FAIL(calc_result_with_all(result, *left_row, subquery_idx, expr_ctx))) {
          LOG_WARN("calc result with all failed", K(ret));
        }
      } else if (T_WITH_ANY == subquery_key_) {
        if (OB_FAIL(calc_result_with_any(result, *left_row, subquery_idx, expr_ctx))) {
          LOG_WARN("calc result with any failed", K(ret));
        }
      } else if (T_WITH_NONE == subquery_key_) {
        // vector compare. scenario: both left right sides are subqueries
        if (OB_UNLIKELY(!left_is_iter_)) {
          ret = OB_ERR_UNEXPECTED;
          LOG_ERROR("left_is_iter_ is wrong", K(ret));
        } else if (OB_FAIL(calc_result_with_none(result, *left_row, subquery_idx, expr_ctx))) {
          LOG_WARN("calc result with none failed", K(ret));
        }
      }
    }
    // for the comparison of subquery, if the left param is row iterator,
    // we must check the row count of the left row iterator
    // the left row iterator can only return one row
    if (OB_SUCC(ret) && left_is_iter_) {
      ObNewRow* tmp_left_row = NULL;
      if (OB_FAIL(left_row_iter->get_next_row(tmp_left_row))) {
        if (OB_LIKELY(OB_ITER_END == ret)) {
          ret = OB_SUCCESS;
        } else {
          LOG_WARN("get next row from iter failed", K(ret));
        }
      } else {
        ret = OB_SUBQUERY_TOO_MANY_ROW;
      }
    }
  }
  return ret;
}

int ObSubQueryRelationalExpr::calc_resultN(
    ObObj& result, const ObObj* param_array, int64_t param_num, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(param_num <= 2)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("param_num is wrong", K(param_array), K(ret));
  } else if (OB_UNLIKELY(left_is_iter_ && right_is_iter_) || OB_ISNULL(expr_ctx.subplan_iters_) ||
             OB_ISNULL(param_array)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("left_is_iter_ and right_is_iter cann't be same true",
        K(param_num),
        K(left_is_iter_),
        K(right_is_iter_),
        K(expr_ctx.subplan_iters_),
        K(param_array),
        K(ret));
  } else {
    int64_t subquery_idx = OB_INVALID_ID;
    ObNewRow tmp_row;
    ObNewRow* left_row = NULL;
    ObNewRowIterator* row_iter = NULL;
    if (left_is_iter_) {
      int64_t left_idx = OB_INVALID_INDEX;
      const ObObj& idx_obj = param_array[0];
      if (OB_FAIL(idx_obj.get_int(left_idx))) {
        LOG_WARN("get left subquery index failed", K(ret), K(idx_obj));
      } else if (OB_ISNULL(row_iter = expr_ctx.subplan_iters_->at(left_idx))) {
        ret = OB_INVALID_ARGUMENT;
        LOG_WARN("get row iterator failed", K(left_idx));
      } else if (OB_FAIL(row_iter->get_next_row(left_row))) {
        if (OB_ITER_END == ret) {
          ret = OB_SUCCESS;
          result.set_null();
        } else {
          LOG_WARN("get next row from left row iterator failed", K(ret));
        }
      } else {
        ObNewRow* tmp_left_row = NULL;
        tmp_row.cells_ = const_cast<ObObj*>(&param_array[1]);
        tmp_row.count_ = param_num - 1;
        if (OB_ISNULL(left_row)) {
          ret = OB_INVALID_ARGUMENT;
          LOG_WARN("left_row is null", K(left_row), K(ret));
        } else if (OB_FAIL(compare_single_row(*left_row, tmp_row, expr_ctx, result))) {
          LOG_WARN("compare single row failed", K(ret));
        } else if (OB_FAIL(row_iter->get_next_row(tmp_left_row))) {
          if (OB_ITER_END == ret) {
            ret = OB_SUCCESS;
          } else {
            LOG_WARN("get next row from iter failed", K(ret));
          }
        } else {
          ret = OB_SUBQUERY_TOO_MANY_ROW;
        }
      }
    } else if (right_is_iter_) {
      tmp_row.cells_ = const_cast<ObObj*>(param_array);
      tmp_row.count_ = param_num - 1;
      left_row = &tmp_row;
      const ObObj& idx_obj = param_array[param_num - 1];
      if (OB_FAIL(idx_obj.get_int(subquery_idx))) {
        LOG_WARN("get subquery index failed", K(ret));
      } else if (T_WITH_ALL == subquery_key_) {
        if (OB_FAIL(calc_result_with_all(result, *left_row, subquery_idx, expr_ctx))) {
          LOG_WARN("calc result with all failed", K(ret));
        }
      } else if (T_WITH_ANY == subquery_key_) {
        if (OB_FAIL(calc_result_with_any(result, *left_row, subquery_idx, expr_ctx))) {
          LOG_WARN("calc result with any failed", K(ret));
        }
      } else if (T_WITH_NONE == subquery_key_) {
        if (OB_FAIL(calc_result_with_none(result, *left_row, subquery_idx, expr_ctx))) {
          LOG_WARN("calc result with none failed", K(ret));
        }
      }
    } else {
      ret = OB_ERR_UNEXPECTED;
      LOG_ERROR("left_is_iter_ and right_is_iter are all false", K(ret));
    }
  }

  return ret;
}

int ObSubQueryRelationalExpr::call(ObObj* stack, int64_t& stack_size, ObExprCtx& expr_ctx) const
{
  ObObj result;
  int ret = OB_SUCCESS;
  if (OB_ISNULL(stack) || OB_UNLIKELY(real_param_num_ < 0)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("stack is null or the param is wrong", K(stack), K(real_param_num_), K(ret));
  } else if (OB_UNLIKELY(real_param_num_ > stack_size)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("stack is null or the param is wrong", K(stack_size), K(real_param_num_), K(ret));
  } else {
    switch (real_param_num_) {
      case 1: {
        if (OB_FAIL(calc_result1(result, stack[stack_size - 1], expr_ctx))) {
          LOG_WARN("fail to calc result1", K(ret), K(stack_size));
        } else {
          stack[stack_size - 1] = result;
        }
        break;
      }
      case 2: {
        if (OB_FAIL(calc_result2(result, stack[stack_size - 2], stack[stack_size - 1], expr_ctx))) {
          LOG_WARN("fail to calc result1", K(ret), K(stack_size));
        } else {
          stack[stack_size - 2] = result;
          stack_size--;
        }
        break;
      }
      default: {
        if (OB_FAIL(this->calc_resultN(result, &stack[stack_size - real_param_num_], real_param_num_, expr_ctx))) {
          LOG_WARN("fail to calc resultN", K(ret), K(stack_size));
        } else {
          stack[stack_size - real_param_num_] = result;
          stack_size -= real_param_num_ - 1;
        }
        break;
      }  // end switch
    }
  }
  return ret;
}

int ObSubQueryRelationalExpr::eval(
    common::ObExprCtx& expr_ctx, common::ObObj& val, common::ObObj* params, int64_t param_num) const
{
  int ret = OB_SUCCESS;
  new (&val) ObObj();
  if (OB_ISNULL(params) || OB_UNLIKELY(param_num < 0) || OB_UNLIKELY(real_param_num_ != param_num)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret), K(param_num), K(real_param_num_));
  } else {
    switch (real_param_num_) {
      case 1: {
        ret = calc_result1(val, params[0], expr_ctx);
        break;
      }
      case 2: {
        ret = calc_result2(val, params[0], params[1], expr_ctx);
        break;
      }
      default: {
        ret = calc_resultN(val, params, param_num, expr_ctx);
      }
    }
    if (OB_FAIL(ret)) {
      LOG_WARN("calc result failed", K(ret), K(param_num_), K(name_), K(get_type_name(type_)));
    }
  }
  return ret;
}

int ObSubQueryRelationalExpr::calc_result_with_none(
    ObObj& result, const ObNewRow& left_row, int64_t subquery_idx, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  ObNewRowIterator* row_iter = NULL;
  ObNewRow* row = NULL;
  if (OB_ISNULL(expr_ctx.subplan_iters_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("expr_ctx.subplan_iters_ is null");
  } else if (OB_UNLIKELY(subquery_idx < 0 || subquery_idx >= expr_ctx.subplan_iters_->count())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery_idx is invalied", K(subquery_idx), "iter count", expr_ctx.subplan_iters_->count());
  } else if (OB_ISNULL(row_iter = expr_ctx.subplan_iters_->at(subquery_idx))) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery result iterator is null");
  } else if (OB_FAIL(row_iter->get_next_row(row))) {
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
      result.set_null();
    } else {
      LOG_WARN("get next row from row iterator failed", K(ret));
    }
  } else {
    if (OB_ISNULL(row)) {
      ret = OB_INVALID_ARGUMENT;
      LOG_WARN("row is null");
    } else if (OB_UNLIKELY(left_row.get_count() != row->get_count())) {
      ret = OB_ERR_UNEXPECTED;
      LOG_ERROR("left_row and right row is not equal");
    } else {
      if (OB_FAIL(compare_single_row(left_row, *row, expr_ctx, result))) {
        LOG_WARN("compare single row failed", K(ret));
      }
    }
  }
  if (OB_SUCC(ret)) {
    if (OB_FAIL(row_iter->get_next_row(row))) {
      if (OB_ITER_END == ret) {
        ret = OB_SUCCESS;
      } else {
        LOG_WARN("get next row from iter failed", K(ret));
      }
    } else {
      ret = OB_SUBQUERY_TOO_MANY_ROW;
    }
  }
  return ret;
}

/**
 * ALL sematics: all true return true. if any false exists, return false
 * if NULL exists, return NULL if other values are true, return false if any values is false
 */
int ObSubQueryRelationalExpr::calc_result_with_all(
    ObObj& result, const ObNewRow& left_row, int64_t subquery_idx, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  ObNewRowIterator* row_iter = NULL;
  ObNewRow* row = NULL;
  ObObj tmp_result;
  bool cnt_null = false;
  if (OB_ISNULL(expr_ctx.subplan_iters_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery_idx is invalied", K(expr_ctx.subplan_iters_), K(ret));
  } else if (OB_UNLIKELY(subquery_idx < 0 || subquery_idx >= expr_ctx.subplan_iters_->count())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery_idx is invalied", K(subquery_idx), "iter count", expr_ctx.subplan_iters_->count());
  } else if (OB_ISNULL(row_iter = expr_ctx.subplan_iters_->at(subquery_idx))) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery result iterator is null");
  } else {
    tmp_result.set_bool(true);  // empty set returns true
    while (OB_SUCC(ret) && OB_SUCC(row_iter->get_next_row(row))) {
      if (OB_ISNULL(row)) {
        ret = OB_INVALID_ARGUMENT;
        LOG_WARN("row is null", K(ret));
      } else if (OB_UNLIKELY(left_row.get_count() != row->get_count())) {
        ret = OB_ERR_UNEXPECTED;
        LOG_ERROR("left_row and right_row is not equal", K(ret));
      } else {
        if (OB_FAIL(compare_single_row(left_row, *row, expr_ctx, tmp_result))) {
          LOG_WARN("compare single row failed", K(ret));
        } else if (OB_UNLIKELY(!tmp_result.is_int32() && !tmp_result.is_null())) {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("result type type is invalid", K(tmp_result));
        } else if (tmp_result.is_false()) {
          break;
        } else if (tmp_result.is_null()) {
          cnt_null = true;
        }
      }
    }
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
    }
    if (OB_SUCC(ret)) {
      if (tmp_result.is_true() && cnt_null) {
        tmp_result.set_null();
      }
      result = tmp_result;
    }
  }
  return ret;
}

/**
 * ANY sematics: return true if any value is true.
 * return NULL if all other values are false
 * return false if all values are false
 */
int ObSubQueryRelationalExpr::calc_result_with_any(
    ObObj& result, const ObNewRow& left_row, int64_t subquery_idx, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  ObNewRowIterator* row_iter = NULL;
  ObNewRow* row = NULL;
  ObObj tmp_result;
  bool cnt_null = false;
  if (OB_ISNULL(expr_ctx.subplan_iters_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery_idx is invalied", K(expr_ctx.subplan_iters_), K(ret));
  } else if (OB_UNLIKELY(subquery_idx < 0 || subquery_idx >= expr_ctx.subplan_iters_->count())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery_idx is invalied", K(subquery_idx), "iter count", expr_ctx.subplan_iters_->count());
  } else if (OB_ISNULL(row_iter = expr_ctx.subplan_iters_->at(subquery_idx))) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("subquery result iterator is null");
  } else {
    tmp_result.set_bool(false);
    while (OB_SUCC(ret) && OB_SUCC(row_iter->get_next_row(row))) {
      if (OB_UNLIKELY(left_row.get_count() != row->get_count())) {
        ret = OB_ERR_UNEXPECTED;
        LOG_ERROR("left_row and right_row is not equal", K(ret));
      } else {
        if (OB_FAIL(compare_single_row(left_row, *row, expr_ctx, tmp_result))) {
          LOG_WARN("compare single row failed", K(ret));
        } else if (OB_UNLIKELY(!tmp_result.is_int32() && !tmp_result.is_null())) {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("result type type is invalid", K(tmp_result), K(ret));
        } else if (tmp_result.is_true()) {
          break;
        } else if (tmp_result.is_null()) {
          cnt_null = true;
        }
      }
    }
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
    }
    if (OB_SUCC(ret)) {
      if (tmp_result.is_false() && cnt_null) {
        tmp_result.set_null();
      }
      result = tmp_result;
    }
  }
  return ret;
}

int ObSubQueryRelationalExpr::compare_obj(common::ObExprCtx& expr_ctx, const ObObj& obj1, const ObObj& obj2,
    const ObExprCalcType& cmp_type, bool is_null_safe, ObObj& result) const
{
  int ret = OB_SUCCESS;
  EXPR_DEFINE_CMP_CTX(cmp_type, is_null_safe, expr_ctx);
  EXPR_DEFINE_CAST_CTX(expr_ctx, CM_WARN_ON_FAIL);
  if (OB_FAIL(ObRelationalExprOperator::compare_cast(result, obj1, obj2, cmp_ctx, cast_ctx, CO_CMP))) {
    LOG_WARN("Compare expression failed", K(ret));
  }
  return ret;
}

int ObSubQueryRelationalExpr::compare_single_row(
    const ObNewRow& left_row, const ObNewRow& right_row, ObExprCtx& expr_ctx, ObObj& result) const
{
  UNUSED(left_row);
  UNUSED(right_row);
  UNUSED(expr_ctx);
  UNUSED(result);
  return OB_NOT_IMPLEMENT;
}

int ObSubQueryRelationalExpr::get_param_types(
    const ObRawExpr& param, const bool is_iter, ObIArray<ObObjMeta>& types) const
{
  int ret = OB_SUCCESS;
  if (param.get_expr_type() == T_OP_ROW) {
    for (int64_t i = 0; OB_SUCC(ret) && i < param.get_param_count(); i++) {
      const ObRawExpr* e = param.get_param_expr(i);
      CK(NULL != e);
      OZ(types.push_back(e->get_result_meta()));
    }
  } else if (param.get_expr_type() == T_REF_QUERY && is_iter) {
    const ObQueryRefRawExpr& ref = static_cast<const ObQueryRefRawExpr&>(param);
    FOREACH_CNT_X(t, ref.get_column_types(), OB_SUCC(ret))
    {
      OZ(types.push_back(*t));
    }
  } else {
    OZ(types.push_back(param.get_result_meta()));
  }
  return ret;
}

int ObSubQueryRelationalExpr::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  int ret = OB_SUCCESS;
  ObSEArray<ObObjMeta, 1> left_types;
  ObSEArray<ObObjMeta, 1> right_types;
  void** funcs = NULL;

  CK(NULL != op_cg_ctx.allocator_);
  CK(2 == raw_expr.get_param_count());
  CK(NULL != raw_expr.get_param_expr(0));
  CK(NULL != raw_expr.get_param_expr(1));

  // row_dimension_ is set for old expr engine in ObExprGeneratorImpl, can not used here
  OZ(get_param_types(*raw_expr.get_param_expr(0), left_is_iter_, left_types));
  OZ(get_param_types(*raw_expr.get_param_expr(1), right_is_iter_, right_types));
  if (OB_FAIL(ret)) {
  } else if (left_types.empty() || left_types.count() != right_types.count()) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("operand cnt mismatch", K(ret), K(left_types.count()), K(right_types.count()));
  } else if (OB_ISNULL(funcs = (void**)op_cg_ctx.allocator_->alloc(sizeof(void*) * left_types.count()))) {
    ret = OB_ALLOCATE_MEMORY_FAILED;
    LOG_WARN("alloc memory failed", K(ret));
  } else {
    rt_expr.inner_func_cnt_ = left_types.count();
    rt_expr.inner_functions_ = funcs;

    for (int64_t i = 0; OB_SUCC(ret) && i < rt_expr.inner_func_cnt_; i++) {
      auto& l = left_types.at(i);
      auto& r = right_types.at(i);
      if (ObDatumFuncs::is_string_type(l.get_type()) && ObDatumFuncs::is_string_type(r.get_type())) {
        CK(l.get_collation_type() == r.get_collation_type());
      }
      if (OB_SUCC(ret)) {
        funcs[i] = (void*)ObExprCmpFuncsHelper::get_datum_expr_cmp_func(
            l.get_type(), r.get_type(), lib::is_oracle_mode(), l.get_collation_type());
        CK(NULL != funcs[i]);
      }
    }
    if (OB_SUCC(ret)) {
      ExtraInfo& info = ExtraInfo::get_info(rt_expr);
      info.subquery_key_ = subquery_key_;
      info.left_is_iter_ = left_is_iter_;
      info.right_is_iter_ = right_is_iter_;

      rt_expr.eval_func_ = &subquery_cmp_eval;
    }
  }
  return ret;
}

int ObSubQueryRelationalExpr::check_exists(const ObExpr& expr, ObEvalCtx& ctx, bool& exists)
{
  int ret = OB_SUCCESS;
  ObDatum* v = NULL;
  ObSubQueryIterator* iter = NULL;
  exists = false;
  if (1 != expr.arg_cnt_) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("unexpected argument count", K(ret));
  } else if (OB_FAIL(expr.args_[0]->eval(ctx, v))) {
    LOG_WARN("NULL subquery ref info returned", K(ret));
  } else if (OB_FAIL(ObExprSubQueryRef::get_subquery_iter(
                 ctx, ObExprSubQueryRef::ExtraInfo::get_info(v->get_int()), iter))) {
    LOG_WARN("get subquery iterator failed", K(ret));
  } else if (OB_ISNULL(iter)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("NULL subquery iterator", K(ret));
  } else if (OB_FAIL(iter->start())) {
    LOG_WARN("start iterate failed", K(ret));
  } else if (OB_FAIL(iter->get_next_row())) {
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
      exists = false;
    } else {
      LOG_WARN("get next row failed", K(ret));
    }
  } else {
    exists = true;
  }
  return ret;
}

int ObSubQueryRelationalExpr::setup_row(ObExpr** expr, ObEvalCtx& ctx, const bool is_iter, const int64_t cmp_func_cnt,
    ObSubQueryIterator*& iter, ObExpr**& row)
{
  int ret = OB_SUCCESS;
  if (is_iter) {
    ObDatum* v = NULL;
    if (OB_FAIL(expr[0]->eval(ctx, v))) {
      LOG_WARN("expr evaluate failed", K(ret));
    } else if (v->is_null()) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("NULL subquery ref info returned", K(ret));
    } else if (OB_FAIL(ObExprSubQueryRef::get_subquery_iter(
                   ctx, ObExprSubQueryRef::ExtraInfo::get_info(v->get_int()), iter))) {
      LOG_WARN("get subquery iterator failed", K(ret));
    } else if (OB_ISNULL(iter) || cmp_func_cnt != iter->get_output().count()) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("NULL subquery iterator", K(ret), KP(iter), K(cmp_func_cnt));
    } else if (OB_FAIL(iter->start())) {
      LOG_WARN("start iterate failed", K(ret));
    } else {
      row = &const_cast<ExprFixedArray&>(iter->get_output()).at(0);
    }
  } else if (T_OP_ROW == expr[0]->type_) {
    if (cmp_func_cnt != expr[0]->arg_cnt_) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("cmp function count mismatch", K(ret), K(cmp_func_cnt), K(*expr[0]));
    } else {
      row = expr[0]->args_;
    }
  } else {
    row = expr;
  }
  return ret;
}

int ObSubQueryRelationalExpr::cmp_one_row(
    const ObExpr& expr, ObDatum& res, ObExpr** l_row, ObEvalCtx& l_ctx, ObExpr** r_row, ObEvalCtx& r_ctx)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(T_OP_SQ_NSEQ == expr.type_)) {
    ret = ObExprNullSafeEqual::ns_equal(expr, res, l_row, l_ctx, r_row, r_ctx);
  } else {
    ret = ObRelationalExprOperator::row_cmp(expr, res, l_row, l_ctx, r_row, r_ctx);
  }
  return ret;
}

int ObSubQueryRelationalExpr::subquery_cmp_eval(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  const ExtraInfo& info = ExtraInfo::get_info(expr);
  ObSubQueryIterator* l_iter = NULL;
  ObSubQueryIterator* r_iter = NULL;
  ObExpr** l_row = NULL;
  ObExpr** r_row = NULL;
  if (2 != expr.arg_cnt_) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("unexpected argument count", K(ret));
  } else if (OB_FAIL(setup_row(expr.args_, ctx, info.left_is_iter_, expr.inner_func_cnt_, l_iter, l_row))) {
    LOG_WARN("setup left row failed", K(ret));
  } else if (OB_FAIL(setup_row(expr.args_ + 1, ctx, info.right_is_iter_, expr.inner_func_cnt_, r_iter, r_row))) {
    LOG_WARN("setup right row failed", K(ret));
  } else if (OB_ISNULL(l_row) || OB_ISNULL(r_row)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("null row", K(ret));
  } else {
    bool l_end = false;
    if (NULL != l_iter) {
      if (OB_FAIL(l_iter->get_next_row())) {
        if (OB_ITER_END == ret) {
          ret = OB_SUCCESS;
          l_end = true;
          // set row to NULL
          for (int64_t i = 0; i < expr.inner_func_cnt_; i++) {
            l_row[i]->locate_expr_datum(ctx).set_null();
            l_row[i]->get_eval_info(ctx).evaluated_ = true;
          }
        } else {
          LOG_WARN("get next row failed", K(ret));
        }
      }
    }
    if (OB_SUCC(ret)) {
      switch (info.subquery_key_) {
        case T_WITH_NONE: {
          ret = subquery_cmp_eval_with_none(expr, ctx, expr_datum, l_row, r_row, r_iter);
          break;
        }
        case T_WITH_ANY: {
          ret = subquery_cmp_eval_with_any(expr, ctx, expr_datum, l_row, r_row, r_iter);
          break;
        }
        case T_WITH_ALL: {
          ret = subquery_cmp_eval_with_all(expr, ctx, expr_datum, l_row, r_row, r_iter);
          break;
        }
      }
    }
    if (OB_SUCC(ret) && NULL != l_iter && !l_end) {
      if (OB_FAIL(l_iter->get_next_row())) {
        if (OB_ITER_END == ret) {
          ret = OB_SUCCESS;
        } else {
          LOG_WARN("get next row failed", K(ret));
        }
      } else {
        // only one row expected for left row
        ret = OB_SUBQUERY_TOO_MANY_ROW;
      }
    }
  }
  return ret;
}

int ObSubQueryRelationalExpr::subquery_cmp_eval_with_none(
    const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res, ObExpr** l_row, ObExpr** r_row, ObSubQueryIterator* r_iter)
{
  int ret = OB_SUCCESS;
  // %l_row, %r_row is checked no need to check.
  // %iter may be NULL for with none.
  bool iter_end = false;
  if (NULL != r_iter) {
    if (OB_FAIL(r_iter->get_next_row())) {
      if (OB_ITER_END == ret) {
        ret = OB_SUCCESS;
        iter_end = true;
        // set row to NULL
        for (int64_t i = 0; i < expr.inner_func_cnt_; i++) {
          r_row[i]->locate_expr_datum(ctx).set_null();
          r_row[i]->get_eval_info(ctx).evaluated_ = true;
        }
      } else {
        LOG_WARN("get next row failed", K(ret));
      }
    }
  }
  if (OB_SUCC(ret)) {
    if (OB_FAIL(cmp_one_row(expr, res, l_row, ctx, r_row, ctx))) {
      LOG_WARN("compare one row failed", K(ret));
    }
  }
  if (NULL != r_iter && !iter_end && OB_SUCC(ret)) {
    if (OB_FAIL(r_iter->get_next_row())) {
      if (OB_ITER_END == ret) {
        ret = OB_SUCCESS;
      } else {
        LOG_WARN("get next row failed", K(ret));
      }
    } else {
      // only one row expected for left row
      ret = OB_SUBQUERY_TOO_MANY_ROW;
    }
  }
  return ret;
}

// copy from ObSubQueryRelationalExpr::calc_result_with_any
int ObSubQueryRelationalExpr::subquery_cmp_eval_with_any(
    const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res, ObExpr** l_row, ObExpr** r_row, ObSubQueryIterator* r_iter)
{
  // %l_row, %r_row is checked no need to check.
  int ret = OB_SUCCESS;
  if (OB_ISNULL(r_iter)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("iter should not be null", K(ret));
  } else {
    bool cnt_null = false;
    res.set_false();
    while (OB_SUCC(ret) && OB_SUCC(r_iter->get_next_row())) {
      // use subquery's eval ctx for right row to avoid ObEvalCtx::alloc_ expanding.
      if (OB_FAIL(cmp_one_row(expr, res, l_row, ctx, r_row, r_iter->get_op().get_eval_ctx()))) {
        LOG_WARN("compare single row failed", K(ret));
      } else if (res.is_true()) {
        break;
      } else if (res.is_null()) {
        cnt_null = true;
      }
    }
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
    }
    if (OB_SUCC(ret)) {
      if (res.is_false() && cnt_null) {
        res.set_null();
      }
    }
  }
  return ret;
}

// copy from ObSubQueryRelationalExpr::calc_result_with_all
int ObSubQueryRelationalExpr::subquery_cmp_eval_with_all(
    const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res, ObExpr** l_row, ObExpr** r_row, ObSubQueryIterator* r_iter)
{
  // %l_row, %r_row is checked no need to check.
  int ret = OB_SUCCESS;
  if (OB_ISNULL(r_iter)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("iter should not be null", K(ret));
  } else {
    bool cnt_null = false;
    res.set_true();
    while (OB_SUCC(ret) && OB_SUCC(r_iter->get_next_row())) {
      // use subquery's eval ctx for right row to avoid ObEvalCtx::alloc_ expanding.
      if (OB_FAIL(cmp_one_row(expr, res, l_row, ctx, r_row, r_iter->get_op().get_eval_ctx()))) {
        LOG_WARN("compare single row failed", K(ret));
      } else if (res.is_false()) {
        break;
      } else if (res.is_null()) {
        cnt_null = true;
      }
    }
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
    }
    if (OB_SUCC(ret)) {
      if (res.is_true() && cnt_null) {
        res.set_null();
      }
    }
  }
  return ret;
}

int ObLogicalExprOperator::calc_result_type1(ObExprResType& type, ObExprResType& type1, ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  UNUSED(type1);
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    type.set_tinyint();
  } else {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  }
  return ret;
}

int ObSubQueryRelationalExpr::calc_result_type2_(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  ObExprResType cmp_type;
  if (OB_SUCC(calc_cmp_type2(cmp_type, type1, type2, type_ctx.get_coll_type()))) {
    type.set_tinyint();
    type.set_calc_collation(cmp_type);
    type.set_calc_type(cmp_type.get_calc_type());
    ObExprOperator::calc_result_flag2(type, type1, type2);
  }
  return ret;
}

int ObLogicalExprOperator::calc_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  UNUSED(type1);
  UNUSED(type2);
  UNUSED(type_ctx);
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    type.set_tinyint();
  } else {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  }
  return ret;
}

int ObLogicalExprOperator::calc_result_type3(ObExprResType& type, ObExprResType& type1, ObExprResType& type2,
    ObExprResType& type3, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  UNUSED(type1);
  UNUSED(type2);
  UNUSED(type3);
  UNUSED(type_ctx);
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    type.set_tinyint();
  } else {
    ret = OB_NOT_SUPPORTED;
  }
  return ret;
}

int ObLogicalExprOperator::calc_result_typeN(
    ObExprResType& type, ObExprResType* types, int64_t param_num, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  UNUSED(types);
  UNUSED(param_num);
  UNUSED(type_ctx);
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    type.set_tinyint();
  } else {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  }
  return ret;
}

int ObLogicalExprOperator::is_true(const ObObj& obj, ObCastMode cast_mode, bool& result)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(ObObjEvaluator::is_true(obj, cast_mode, result))) {
    // ugly...really ugly in order to be compatible with mysql.
    if (OB_ERR_TRUNCATED_WRONG_VALUE_FOR_FIELD == ret) {
      // slow path. need twice calls for is_true_. not efficient but we will not reach here too frequently
      ret = ObObjEvaluator::is_true(obj, CM_WARN_ON_FAIL | CM_NO_RANGE_CHECK, result);
    }
  }
  return ret;
}

int ObVectorExprOperator::calc_result_type1(ObExprResType& type, ObExprResType& type1, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_ERR_UNEXPECTED;
  UNUSED(type);
  UNUSED(type1);
  UNUSED(type_ctx);
  LOG_WARN("operator in should not come here", K(ret));
  return ret;
}

int ObVectorExprOperator::calc_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  ObArenaAllocator alloc;
  ObExprResType types[2] = {alloc, alloc};
  types[0] = type1;
  types[1] = type2;
  return calc_result_typeN(type, types, 2, type_ctx);
}

int ObVectorExprOperator::calc_result_type3(ObExprResType& type, ObExprResType& type1, ObExprResType& type2,
    ObExprResType& type3, ObExprTypeCtx& type_ctx) const
{
  ObArenaAllocator alloc;
  ObExprResType types[3] = {alloc, alloc, alloc};
  types[0] = type1;
  types[1] = type2;
  types[2] = type3;
  return calc_result_typeN(type, types, 3, type_ctx);
}

int ObVectorExprOperator::calc_result_typeN(
    ObExprResType& type, ObExprResType* types, int64_t param_num, ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num <= 0 || 0 >= row_dimension_) || OB_ISNULL(type_ctx.get_session())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("types is null or invalid param num or row_dimension_",
        K(types),
        K(param_num),
        K(row_dimension_),
        K(type_ctx.get_session()),
        K(ret));
  } else {
    int64_t left_start_idx = 0;
    int64_t right_start_idx = row_dimension_;
    int64_t right_element_count = param_num / row_dimension_ - 1;
    ObExprResType tmp_res_type;
    if (OB_FAIL(type.init_row_dimension(param_num))) {
      LOG_WARN("fail to init row dimension", K(ret));
    }
    bool is_nested_table_elem = true;
    // in maybe nest table, (nt1 in (nt2, nt3, nt4))
    for (int64_t k = 0; lib::is_oracle_mode() && k < param_num; ++k) {
      is_nested_table_elem &= types[k].is_ext();
    }
    if (lib::is_oracle_mode() && is_nested_table_elem) {
      if (row_dimension_ != 1) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("in condition of nest table can only have one left operator", K(ret), K(row_dimension_), K(param_num));
      } else {
        // extend type is unknown yet, get at runtime
      }
    } else {
      for (int64_t i = 0; OB_SUCC(ret) && i < right_element_count; ++i, right_start_idx += row_dimension_) {
        tmp_res_type.reset();
        for (int64_t j = 0; OB_SUCC(ret) && j < row_dimension_; ++j) {
          if (OB_FAIL(ObVectorExprOperator::calc_result_type2_(
                  tmp_res_type, types[left_start_idx + j], types[right_start_idx + j], type_ctx))) {
            LOG_WARN("failed to calc result types", K(ret));
          } else if (OB_FAIL(type.get_row_calc_cmp_types().push_back(tmp_res_type.get_calc_meta()))) {
            LOG_WARN("failed to push back cmp type", K(ret));
          }
        }
      }
    }
    // overall result type
    if (OB_SUCC(ret)) {
      type.set_int32();
      type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY[type.get_type()].precision_);
      type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY[type.get_type()].scale_);
      ObExprOperator::calc_result_flagN(type, types, param_num);
    }
  }
  return ret;
}

int ObVectorExprOperator::calc_result_type2_(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  ObExprResType cmp_type;
  if (OB_ISNULL(type_ctx.get_session())) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("session is null", K(ret));
  } else if (OB_FAIL(calc_cmp_type2(cmp_type, type1, type2, type_ctx.get_coll_type()))) {
    LOG_WARN("calc cmp type2 failed", K(ret));
  } else {
    type.set_int();  // not tinyint, compatiable with MySQL
    type.set_calc_collation(cmp_type);
    type.set_calc_type(cmp_type.get_calc_type());
    ObExprOperator::calc_result_flag2(type, type1, type2);

    if (type_ctx.get_session()->use_static_typing_engine()) {
      bool need_no_cast = ObRelationalExprOperator::can_cmp_without_cast(type1, type2,
                                                            CO_EQ, *type_ctx.get_session());
      type1.set_calc_type(need_no_cast ? type1.get_type() : cmp_type.get_calc_type());
      type2.set_calc_type(need_no_cast ? type2.get_type() : cmp_type.get_calc_type());
      if (ob_is_string_type(cmp_type.get_calc_type())) {
        if (OB_ISNULL(type_ctx.get_session())) {
          ret = OB_INVALID_ARGUMENT;
          LOG_WARN("invalid null session", K(type_ctx.get_session()));
        } else if (share::is_oracle_mode()) {
          // oracle charset deduce
          ObExprResType tmp_res_type;
          ObSEArray<ObExprResType*, 2> tmp_param_array;
          if (OB_FAIL(tmp_param_array.push_back(&type1)) || OB_FAIL(tmp_param_array.push_back(&type2))) {
            LOG_WARN("failed to push back element", K(ret));
          } else if (OB_FAIL(aggregate_string_type_and_charset_oracle(
                         *type_ctx.get_session(), tmp_param_array, tmp_res_type))) {
            LOG_WARN("unexpected failed", K(ret));
          } else if (OB_FAIL(deduce_string_param_calc_type_and_charset(
                         *type_ctx.get_session(), tmp_res_type, tmp_param_array))) {
            LOG_WARN("unexpected failed", K(ret));
          }
        } else {
          type1.set_calc_collation_type(cmp_type.get_calc_collation_type());
          type2.set_calc_collation_type(cmp_type.get_calc_collation_type());
        }
      } else if (ObRawType == cmp_type.get_calc_type()) {
        type1.set_calc_collation_type(CS_TYPE_BINARY);
        type2.set_calc_collation_type(CS_TYPE_BINARY);
      }
    }
  }
  return ret;
}

int ObStringExprOperator::convert_result_collation(
    const ObExprResType& result_type, ObObj& in_obj, ObIAllocator* allocator)
{
  int ret = OB_SUCCESS;
  ObCollationType in_collation = in_obj.get_collation_type();
  ObString out_str;
  if (OB_UNLIKELY(!in_obj.is_string_or_lob_locator_type() || !result_type.is_string_or_lob_locator_type() ||
                  !ObCharset::is_valid_collation(in_collation) ||
                  !ObCharset::is_valid_collation(result_type.get_collation_type()) || OB_ISNULL(allocator))) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret), K(in_obj), K(result_type));
  } else if (OB_FAIL(ObExprUtil::convert_string_collation(
                 in_obj.get_string(), in_collation, out_str, result_type.get_collation_type(), *allocator))) {
    LOG_WARN("convert_string_collation failed", K(ret), K(in_obj), K(result_type));
  } else {
    in_obj.set_string(result_type.get_type(), out_str);
    in_obj.set_collation(result_type);
  }
  return ret;
}

// for calc result type and length for function date_format and time_format.
void ObStringExprOperator::calc_temporal_format_result_length(
      ObExprResType &type, const ObExprResType &format) const
{
  const int64_t VARCHAR_RES_MAX_PARAM_LENGTH = 17;
  const int64_t TEXT_RES_MAX_PARAM_LENGTH = 728;
  const int64_t MAX_VARCHAR_BUFFER_SIZE = 256;
  if (ob_is_string_tc(format.get_type())) {
    // consistent with Mysql, result_length / format_length = 30
    const int64_t ratio = 30;
    if (format.get_length() <= VARCHAR_RES_MAX_PARAM_LENGTH) {
      type.set_varchar();
      type.set_length(format.get_length() * ratio);
    } else if (format.get_length() < TEXT_RES_MAX_PARAM_LENGTH) {
      type.set_type(ObTextType);
    } else {
      type.set_type(ObLongTextType);
    }
  } else if (ob_is_text_tc(format.get_type())) {
    type.set_type(ObTinyTextType == format.get_type() ? ObTextType : ObLongTextType);
  } else {
    type.set_varchar();
    type.set_length(MAX_VARCHAR_BUFFER_SIZE);
  }
  if (is_mysql_mode() && ob_is_text_tc(type.get_type())) {
    const int32_t mbmaxlen = 4;
    const int32_t default_text_length = ObAccuracy::DDL_DEFAULT_ACCURACY[type.get_type()].get_length() / mbmaxlen;
    // need to set a correct length for text tc in mysql mode
    type.set_length(default_text_length);
  }
}

ObObjType ObStringExprOperator::get_result_type_mysql(int64_t char_length) const
{
  /*
    drop table t1;
    drop table t2;
    create table t1 (
      col_varchar    varchar(1),
      col_varbinary  varbinary(1),
      col_char       char(1),
      col_binary     binary(1),
      col_tinytext   tinytext,
      col_tinyblob   tinyblob,
      col_mediumtext mediumtext,
      col_mediumblob mediumblob,
      col_text       text,
      col_blob       blob,
      col_count      bigint);
    create table t2 as
    select repeat('啊',   512),
           repeat('a',    513),
           repeat('啊', 21845),
           repeat('a',  21846),
           repeat(col_varchar, '512'),
           repeat(col_varchar, '513'),
           repeat(col_varchar, 65535),
           repeat(col_varchar, 65536),
           repeat(col_varbinary, '512'),
           repeat(col_varbinary, '513'),
           repeat(col_varbinary, 65535),
           repeat(col_varbinary, 65536),
           repeat(col_char, '512'),
           repeat(col_char, '513'),
           repeat(col_char, 65535),
           repeat(col_char, 65536),
           repeat(col_binary, '512'),
           repeat(col_binary, '513'),
           repeat(col_binary, 65535),
           repeat(col_binary, 65536),
           repeat(col_tinytext,   2),
           repeat(col_tinytext,   3),
           repeat(col_tinytext, 257),
           repeat(col_tinytext, 258),
           repeat(col_tinyblob,   2),
           repeat(col_tinyblob,   3),
           repeat(col_tinyblob, 257),
           repeat(col_tinyblob, 258),
           repeat(col_mediumtext, 1),
           repeat(col_mediumblob, 1),
           repeat(col_text, 1),
           repeat(col_text, 2),
           repeat(col_blob, 1),
           repeat(col_blob, 2),
           repeat('啊', col_count),
           repeat('a', col_count),
           repeat(col_varchar, col_count),
           repeat(col_varbinary, col_count),
           repeat(col_char, col_count),
           repeat(col_binary, col_count),
           repeat(col_tinytext, col_count),
           repeat(col_tinyblob, col_count),
           repeat(col_mediumtext, col_count),
           repeat(col_mediumblob, col_count),
           repeat(col_text, col_count),
           repeat(col_blob, col_count)
    from t1;
    desc t2;

    MySQL> desc t2;
    +-----------------------------------+----------------+------+-----+---------+-------+
    | Field                             | Type           | Null | Key | Default | Extra |
    +-----------------------------------+----------------+------+-----+---------+-------+
    | repeat('啊',   512)               | varchar(512)   | YES  |     | NULL    |       |
    | repeat('a',    513)               | text           | YES  |     | NULL    |       |
    | repeat('啊', 21845)               | text           | YES  |     | NULL    |       |
    | repeat('a',  21846)               | longtext       | YES  |     | NULL    |       |
    | repeat(col_varchar, '512')        | varchar(512)   | YES  |     | NULL    |       |
    | repeat(col_varchar, '513')        | text           | YES  |     | NULL    |       |
    | repeat(col_varchar, 65535)        | text           | YES  |     | NULL    |       |
    | repeat(col_varchar, 65536)        | longtext       | YES  |     | NULL    |       |
    | repeat(col_varbinary, '512')      | varbinary(512) | YES  |     | NULL    |       |
    | repeat(col_varbinary, '513')      | blob           | YES  |     | NULL    |       |
    | repeat(col_varbinary, 65535)      | blob           | YES  |     | NULL    |       |
    | repeat(col_varbinary, 65536)      | longblob       | YES  |     | NULL    |       |
    | repeat(col_char, '512')           | varchar(512)   | YES  |     | NULL    |       |
    | repeat(col_char, '513')           | text           | YES  |     | NULL    |       |
    | repeat(col_char, 65535)           | text           | YES  |     | NULL    |       |
    | repeat(col_char, 65536)           | longtext       | YES  |     | NULL    |       |
    | repeat(col_binary, '512')         | varbinary(512) | YES  |     | NULL    |       |
    | repeat(col_binary, '513')         | blob           | YES  |     | NULL    |       |
    | repeat(col_binary, 65535)         | blob           | YES  |     | NULL    |       |
    | repeat(col_binary, 65536)         | longblob       | YES  |     | NULL    |       |
    | repeat(col_tinytext,   2)         | varchar(510)   | YES  |     | NULL    |       |
    | repeat(col_tinytext,   3)         | text           | YES  |     | NULL    |       |
    | repeat(col_tinytext, 257)         | text           | YES  |     | NULL    |       |
    | repeat(col_tinytext, 258)         | longtext       | YES  |     | NULL    |       |
    | repeat(col_tinyblob,   2)         | varbinary(510) | YES  |     | NULL    |       |
    | repeat(col_tinyblob,   3)         | blob           | YES  |     | NULL    |       |
    | repeat(col_tinyblob, 257)         | blob           | YES  |     | NULL    |       |
    | repeat(col_tinyblob, 258)         | longblob       | YES  |     | NULL    |       |
    | repeat(col_mediumtext, 1)         | longtext       | YES  |     | NULL    |       |
    | repeat(col_mediumblob, 1)         | longblob       | YES  |     | NULL    |       |
    | repeat(col_text, 1)               | text           | YES  |     | NULL    |       |
    | repeat(col_text, 2)               | longtext       | YES  |     | NULL    |       |
    | repeat(col_blob, 1)               | blob           | YES  |     | NULL    |       |
    | repeat(col_blob, 2)               | longblob       | YES  |     | NULL    |       |
    | repeat('啊', col_count)           | longtext       | YES  |     | NULL    |       |
    | repeat('a', col_count)            | longtext       | YES  |     | NULL    |       |
    | repeat(col_varchar, col_count)    | longtext       | YES  |     | NULL    |       |
    | repeat(col_varbinary, col_count)  | longblob       | YES  |     | NULL    |       |
    | repeat(col_char, col_count)       | longtext       | YES  |     | NULL    |       |
    | repeat(col_binary, col_count)     | longblob       | YES  |     | NULL    |       |
    | repeat(col_tinytext, col_count)   | longtext       | YES  |     | NULL    |       |
    | repeat(col_tinyblob, col_count)   | longblob       | YES  |     | NULL    |       |
    | repeat(col_mediumtext, col_count) | longtext       | YES  |     | NULL    |       |
    | repeat(col_mediumblob, col_count) | longblob       | YES  |     | NULL    |       |
    | repeat(col_text, col_count)       | longtext       | YES  |     | NULL    |       |
    | repeat(col_blob, col_count)       | longblob       | YES  |     | NULL    |       |
    +-----------------------------------+----------------+------+-----+---------+-------+
    46 rows in set (0.00 sec)

   * summarize the rules that mysql decide the result type of some string functions,
   * which mainly depend on the MAX CHAR LENGTH of result:
   * 1. less than or equal to 512: varchar, otherwise
   * 2. less than or equal to 65535: text or blob, otherwise
   * 3. longtext or longblob (including UNKNOWN length when count param is not const).
   * do not care about the input data type, or whether the input is column or const.
   *
   * ATTENTION! we will ignore these exceptions:
   * 1. repeat('啊', 21845) => text, repeat('a',  21846) => longtext. we can not
   *    understand the magic numbers 21845 and 21846.
   * 2. repeat(col_mediumtext, 1) => longtext, repeat(col_mediumblob, 1) => longblob.
   *    text or blob is enough, see:
   *    repeat(col_text, 1) => text, repeat(col_blob, 1) => blob.
   */
  ObObjType res_type = ObMaxType;
  if (char_length <= MAX_CHAR_LENGTH_FOR_VARCAHR_RESULT) {
    res_type = ObVarcharType;
  } else if (char_length <= MAX_CHAR_LENGTH_FOR_TEXT_RESULT) {
    res_type = ObTextType;
  } else {
    res_type = ObLongTextType;
  }
  return res_type;
}

// for static_typing_engine
int ObBitwiseExprOperator::set_calc_type(ObExprResType& type)
{
  if (lib::is_oracle_mode()) {
    type.set_calc_type(ObNumberType);
    type.set_precision(DEFAULT_NUMBER_PRECISION_FOR_INTEGER);
    type.set_scale(DEFAULT_NUMBER_SCALE_FOR_INTEGER);
  } else {
    if (ObNumberType == type.get_type()) {
      type.set_calc_type(ObNumberType);
      type.set_precision(DEFAULT_NUMBER_PRECISION_FOR_INTEGER);
      type.set_scale(DEFAULT_NUMBER_SCALE_FOR_INTEGER);
    } else {
      type.set_calc_type(ObUInt64Type);
      type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].precision_);
      type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].scale_);
    }
  }
  return OB_SUCCESS;
}

int ObBitwiseExprOperator::calc_result_type1(ObExprResType& type, ObExprResType& type1, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    if (is_oracle_mode()) {
      type.set_number();
      type.set_precision(DEFAULT_NUMBER_PRECISION_FOR_INTEGER);
      type.set_scale(DEFAULT_NUMBER_SCALE_FOR_INTEGER);
    } else {
      type.set_uint64();
      type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].precision_);
      type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].scale_);
    }
    ObExprOperator::calc_result_flag1(type, type1);
    const ObSQLSessionInfo* session = dynamic_cast<const ObSQLSessionInfo*>(type_ctx.get_session());
    if (OB_ISNULL(session)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("cast basic session to sql session failed", K(ret));
    } else if (session->use_static_typing_engine()) {
      if (OB_FAIL(set_calc_type(type1))) {
        LOG_WARN("set_calc_type for type1 failed", K(ret), K(type1));
      } else {
        ObCastMode cm = lib::is_oracle_mode() ? CM_NONE : CM_STRING_INTEGER_TRUNC;
        type_ctx.set_cast_mode(type_ctx.get_cast_mode() | CM_NO_RANGE_CHECK | cm);
      }
    }
  } else {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  }
  return ret;
}

int ObBitwiseExprOperator::calc_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    if (is_oracle_mode()) {
      type.set_number();
      type.set_precision(DEFAULT_NUMBER_PRECISION_FOR_INTEGER);
      type.set_scale(DEFAULT_NUMBER_SCALE_FOR_INTEGER);
    } else {
      type.set_uint64();
      type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].precision_);
      type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].scale_);
    }
    ObExprOperator::calc_result_flag2(type, type1, type2);
    const ObSQLSessionInfo* session = dynamic_cast<const ObSQLSessionInfo*>(type_ctx.get_session());
    if (OB_ISNULL(session)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("cast basic session to sql session failed", K(ret));
    } else if (session->use_static_typing_engine()) {
      if (OB_FAIL(set_calc_type(type1))) {
        LOG_WARN("set_calc_type for type1 failed", K(ret), K(type1));
      } else if (OB_FAIL(set_calc_type(type2))) {
        LOG_WARN("set_calc_type for type2 failed", K(ret), K(type2));
      } else {
        ObCastMode cm = lib::is_oracle_mode() ? CM_NONE : CM_STRING_INTEGER_TRUNC;
        type_ctx.set_cast_mode(type_ctx.get_cast_mode() | CM_NO_RANGE_CHECK | cm);
      }
    }
  } else {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  }
  LOG_DEBUG("bitwise calc type2 done", K(ret), K(type), K(type1), K(type2));
  return ret;
}

int ObBitwiseExprOperator::calc_result_type3(ObExprResType& type, ObExprResType& type1, ObExprResType& type2,
    ObExprResType& type3, ObExprTypeCtx& type_ctx) const
{
  UNUSED(type_ctx);
  int ret = OB_SUCCESS;
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    if (is_oracle_mode()) {
      type.set_number();
      type.set_precision(DEFAULT_NUMBER_PRECISION_FOR_INTEGER);
      type.set_scale(DEFAULT_NUMBER_SCALE_FOR_INTEGER);
    } else {
      type.set_uint64();
      type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].precision_);
      type.set_scale(ObAccuracy::DDL_DEFAULT_ACCURACY[ObUInt64Type].scale_);
    }
    ObExprOperator::calc_result_flag3(type, type1, type2, type3);
  } else {
    ret = OB_NOT_SUPPORTED;
  }
  return ret;
}

int ObBitwiseExprOperator::calc_result_typeN(
    ObExprResType& type, ObExprResType* types, int64_t param_num, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  UNUSED(types);
  UNUSED(param_num);
  UNUSED(type_ctx);
  if (OB_LIKELY(NOT_ROW_DIMENSION == row_dimension_)) {
    if (is_oracle_mode()) {
      type.set_number();
      type.set_precision(DEFAULT_NUMBER_PRECISION_FOR_INTEGER);
      type.set_scale(DEFAULT_NUMBER_SCALE_FOR_INTEGER);
    } else {
      type.set_uint64();
      type.set_scale(DEFAULT_SCALE_FOR_INTEGER);
    }
    ObExprOperator::calc_result_flagN(type, types, param_num);
  } else {
    ret = OB_ERR_INVALID_TYPE_FOR_OP;
  }
  return ret;
}

int ObBitwiseExprOperator::calc_(
    ObObj& res, const ObObj& obj1, const ObObj& obj2, ObExprCtx& expr_ctx, BitOperator op) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("expr_ctx.calc_buf_ is null", K(expr_ctx.calc_buf_), K(ret));
  } else if (OB_UNLIKELY(op < 0 || op >= BIT_MAX)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid index", K(obj1), K(obj2), K(op));
  } else if (OB_UNLIKELY(obj1.is_null() || obj2.is_null())) {
    res.set_null();
  } else {
    if (share::is_mysql_mode()) {
      uint64_t uint64_v1 = 0;
      uint64_t uint64_v2 = 0;
      if (OB_FAIL(get_uint64(obj1, expr_ctx, true, uint64_v1))) {
        LOG_WARN("fail to get uint64", K(obj1), K(ret));
      } else if (OB_FAIL(get_uint64(obj2, expr_ctx, true, uint64_v2))) {
        LOG_WARN("fail to get uint64", K(obj2), K(ret));
      } else {
        // Do not worry too much about the efficiency
        // although we calc 5 results while only one is used here.
        // Bit operations take little time
        uint64_t bit_op_res[BIT_MAX] = {uint64_v1 & uint64_v2,
            uint64_v1 | uint64_v2,
            uint64_v1 ^ uint64_v2,
            uint64_v2 < sizeof(uint64_t) * 8 ? uint64_v1 << uint64_v2 : 0,
            uint64_v2 < sizeof(uint64_t) * 8 ? uint64_v1 >> uint64_v2 : 0};
        res.set_uint64(bit_op_res[op]);
      }
    } else {
      if (op != BIT_AND) {
        ret = OB_NOT_SUPPORTED;  // oracle mode support bitand op only
        LOG_WARN("not support bit op in oracle mode", K(ret), K(op));
      } else {
        int64_t int64_v1 = 0;
        int64_t int64_v2 = 0;
        if (OB_FAIL(get_int64(obj1, expr_ctx, false, int64_v1))) {
          LOG_WARN("failed to get int64", K(obj1), K(ret));
        } else if (OB_FAIL(get_int64(obj2, expr_ctx, false, int64_v2))) {
          LOG_WARN("failed to get int64", K(obj2), K(ret));
        } else {
          ObNumber result;
          if (OB_FAIL(result.from((int64_v1 & int64_v2), *expr_ctx.calc_buf_))) {
            LOG_WARN("get ObNumber from int64 failed", K(ret), K(int64_v1 & int64_v2));
          } else {
            res.set_number(result);
          }
        }
      }
    }
  }
  return ret;
}

int ObBitwiseExprOperator::calc_result2_mysql(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res_datum)

{
  int ret = OB_SUCCESS;
  ObDatum* left = NULL;
  ObDatum* right = NULL;
  const BitOperator op = static_cast<const BitOperator>(expr.extra_);
  ObCastMode cast_mode = CM_NONE;
  if (OB_UNLIKELY(op < 0 || op >= BIT_MAX)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret), K(op));
  } else if (OB_FAIL(expr.args_[0]->eval(ctx, left))) {
    LOG_WARN("eval arg 0 failed", K(ret));
  } else if (left->is_null()) {
    res_datum.set_null();
  } else if (OB_FAIL(expr.args_[1]->eval(ctx, right))) {
    LOG_WARN("eval arg 1 failed", K(ret));
  } else if (right->is_null()) {
    res_datum.set_null();
  } else if (OB_FAIL(ObSQLUtils::get_default_cast_mode(false, 0, ctx.exec_ctx_.get_my_session(), cast_mode))) {
    LOG_WARN("get default cast mode failed", K(ret));
  } else {
    uint64_t left_uint = 0;
    uint64_t right_uint = 0;
    void* get_uint_func0 = NULL;
    void* get_uint_func1 = NULL;
    const ObObjType& left_type = expr.args_[0]->datum_meta_.type_;
    const ObObjType& right_type = expr.args_[1]->datum_meta_.type_;
    if (OB_FAIL(choose_get_int_func(left_type, get_uint_func0))) {
      LOG_WARN("choose_get_int_func failed", K(ret), K(left_type));
    } else if (OB_FAIL((reinterpret_cast<GetUIntFunc>(get_uint_func0)(*left, true, left_uint, cast_mode)))) {
      LOG_WARN("get uint64 failed", K(ret), K(*left));
    } else if (OB_FAIL(choose_get_int_func(right_type, get_uint_func1))) {
      LOG_WARN("choose_get_int_func failed", K(ret), K(right_type));
    } else if (OB_FAIL((reinterpret_cast<GetUIntFunc>(get_uint_func1)(*right, true, right_uint, cast_mode)))) {
      LOG_WARN("get uint64 failed", K(ret), K(*right));
    } else {
      // Do not worry too much about the efficiency
      // although we calc 5 results while only one is used here.
      // Bit operations take little time
      uint64_t bit_op_res[BIT_MAX] = {left_uint & right_uint,
          left_uint | right_uint,
          left_uint ^ right_uint,
          right_uint < sizeof(uint64_t) * 8 ? left_uint << right_uint : 0,
          right_uint < sizeof(uint64_t) * 8 ? left_uint >> right_uint : 0};
      res_datum.set_uint(bit_op_res[op]);
    }
  }
  return ret;
}

int ObBitwiseExprOperator::calc_result2_oracle(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res_datum)

{
  int ret = OB_SUCCESS;
  ObDatum* left = NULL;
  ObDatum* right = NULL;
  const BitOperator op = static_cast<const BitOperator>(expr.extra_);
  ObCastMode cast_mode = CM_NONE;
  if (OB_UNLIKELY(BIT_AND != op)) {
    ret = OB_NOT_SUPPORTED;
    LOG_WARN("unsupported bit operator", K(ret), K(op), K(BIT_AND));
  } else if (OB_FAIL(expr.args_[0]->eval(ctx, left))) {
    LOG_WARN("eval arg 0 failed", K(ret));
  } else if (left->is_null()) {
    res_datum.set_null();
  } else if (OB_FAIL(expr.args_[1]->eval(ctx, right))) {
    LOG_WARN("eval arg 1 failed", K(ret));
  } else if (right->is_null()) {
    res_datum.set_null();
  } else if (OB_FAIL(ObSQLUtils::get_default_cast_mode(false, 0, ctx.exec_ctx_.get_my_session(), cast_mode))) {
    LOG_WARN("get default cast mode failed", K(ret));
  } else {
    int64_t left_int = 0;
    int64_t right_int = 0;
    ObNumStackOnceAlloc tmp_alloc;
    ObNumber result;
    void* get_int_func0 = NULL;
    void* get_int_func1 = NULL;
    const ObObjType& left_type = expr.args_[0]->datum_meta_.type_;
    const ObObjType& right_type = expr.args_[1]->datum_meta_.type_;
    if (OB_FAIL(choose_get_int_func(left_type, get_int_func0))) {
      LOG_WARN("choose_get_int_func failed", K(ret), K(left_type));
    } else if (OB_FAIL((reinterpret_cast<GetIntFunc>(get_int_func0)(*left, false, left_int, cast_mode)))) {
      LOG_WARN("get uint64 failed", K(ret), K(*left));
    } else if (OB_FAIL(choose_get_int_func(right_type, get_int_func1))) {
      LOG_WARN("choose_get_int_func failed", K(ret), K(right_type));
    } else if (OB_FAIL((reinterpret_cast<GetIntFunc>(get_int_func1)(*right, false, right_int, cast_mode)))) {
      LOG_WARN("get uint64 failed", K(ret), K(*right));
    } else if (OB_FAIL(result.from((left_int & right_int), tmp_alloc))) {
      LOG_WARN("get ObNumber from int64 failed", K(ret), K(left_int & right_int));
    } else {
      res_datum.set_number(result);
    }
  }
  return ret;
}

// for static typing engine
int ObBitwiseExprOperator::cg_bitwise_expr(
    ObExprCGCtx& expr_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr, const BitOperator op)
{
  int ret = OB_SUCCESS;
  UNUSED(raw_expr);
  // bit count, bit neg: 1 == arg_cnt_
  // bit and/or/xor/left shift/right shift: 2 == arg_cnt_
  if (OB_ISNULL(rt_expr.args_) || OB_ISNULL(expr_cg_ctx.allocator_) ||
      OB_UNLIKELY(1 != rt_expr.arg_cnt_ && 2 != rt_expr.arg_cnt_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("args_ is NULL or arg_cnt_ is invalid", K(ret), K(rt_expr));
  } else {
    rt_expr.extra_ = static_cast<uint64_t>(op);
    if (2 == rt_expr.arg_cnt_) {
      if (lib::is_oracle_mode()) {
        rt_expr.eval_func_ = ObBitwiseExprOperator::calc_result2_oracle;
      } else {
        rt_expr.eval_func_ = ObBitwiseExprOperator::calc_result2_mysql;
      }
    } else {
      // must be set in its cg_expr method
      rt_expr.eval_func_ = NULL;
    }
  }
  return ret;
}

int ObBitwiseExprOperator::choose_get_int_func(const ObObjType type, void*& out_func)
{
  int ret = OB_SUCCESS;
  if (ObNumberTC == ob_obj_type_class(type)) {
    if (lib::is_oracle_mode()) {
      out_func = reinterpret_cast<void*>(ObBitwiseExprOperator::get_int64_from_number_type);
    } else {
      out_func = reinterpret_cast<void*>(ObBitwiseExprOperator::get_uint64_from_number_type);
    }
  } else {
    if (lib::is_oracle_mode()) {
      out_func = reinterpret_cast<void*>(ObBitwiseExprOperator::get_int64_from_int_tc);
    } else {
      out_func = reinterpret_cast<void*>(ObBitwiseExprOperator::get_uint64_from_int_tc);
    }
  }
  return ret;
}

int ObBitwiseExprOperator::get_int64_from_int_tc(
    const ObDatum& datum, bool is_round, int64_t& out, const ObCastMode& cast_mode)
{
  int ret = OB_SUCCESS;
  UNUSED(is_round);
  UNUSED(cast_mode);
  out = datum.get_int();
  return ret;
}

int ObBitwiseExprOperator::get_uint64_from_int_tc(
    const ObDatum& datum, bool is_round, uint64_t& out, const ObCastMode& cast_mode)
{
  int ret = OB_SUCCESS;
  UNUSED(is_round);
  UNUSED(cast_mode);
  out = datum.get_uint();
  return ret;
}

int ObBitwiseExprOperator::get_int64_from_number_type(
    const ObDatum& datum, bool is_round, int64_t& out, const ObCastMode& cast_mode)
{
  int ret = OB_SUCCESS;
  int64_t tmp_int = 0;
  ObNumStackAllocator<> num_allocator;
  number::ObNumber nmb;
  if (OB_FAIL(nmb.from(datum.get_number(), num_allocator))) {
    LOG_WARN("number copy failed", K(ret));
  } else if (OB_UNLIKELY(!nmb.is_integer() && OB_FAIL(is_round ? nmb.round(0) : nmb.trunc(0)))) {
    LOG_WARN("round/trunc failed", K(ret), K(is_round), K(nmb));
  } else if (nmb.is_valid_int64(tmp_int)) {
    out = tmp_int;
  } else {
    ret = OB_ERR_TRUNCATED_WRONG_VALUE;
    if (CM_IS_WARN_ON_FAIL(cast_mode)) {
      ret = OB_SUCCESS;
      out = 0;
    }
  }
  return ret;
}

int ObBitwiseExprOperator::get_uint64_from_number_type(
    const ObDatum& datum, bool is_round, uint64_t& out, const ObCastMode& cast_mode)
{
  int ret = OB_SUCCESS;
  ObNumStackAllocator<> num_allocator;
  number::ObNumber nmb;
  int64_t tmp_int = 0;
  uint64_t tmp_uint = 0;
  if (OB_FAIL(nmb.from(datum.get_number(), num_allocator))) {
    LOG_WARN("number copy failed", K(ret));
  } else if (OB_UNLIKELY(!nmb.is_integer() && OB_FAIL(is_round ? nmb.round(0) : nmb.trunc(0)))) {
    LOG_WARN("round/trunc failed", K(ret), K(is_round), K(nmb));
  } else if (nmb.is_valid_int64(tmp_int)) {
    out = static_cast<uint64_t>(tmp_int);
  } else if (nmb.is_valid_uint64(tmp_uint)) {
    out = tmp_uint;
  } else {
    ret = OB_ERR_TRUNCATED_WRONG_VALUE;
    if (CM_IS_WARN_ON_FAIL(cast_mode)) {
      ret = OB_SUCCESS;
      out = 0;
    }
  }
  return ret;
}

int ObBitwiseExprOperator::get_int64(const ObObj& obj, ObExprCtx& expr_ctx, bool is_round, int64_t& out)
{
  int ret = OB_SUCCESS;
  out = 0;
  ObObjType type = obj.get_type();
  if (OB_LIKELY(ob_is_int_tc(type))) {
    out = obj.get_int();
  } else if (OB_UNLIKELY(obj.is_number())) {
    int64_t tmp_int = 0;
    number::ObNumber nmb;
    EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
    if (OB_FAIL(nmb.from(obj.get_number(), cast_ctx))) {
      LOG_WARN("deep copy failed", K(ret), K(is_round), K(obj));
    } else if (OB_UNLIKELY(!nmb.is_integer() && OB_FAIL(is_round ? nmb.round(0) : nmb.trunc(0)))) {
      LOG_WARN("round/trunc failed", K(ret), K(is_round), K(nmb));
    } else if (nmb.is_valid_int64(tmp_int)) {
      out = tmp_int;
    } else {
      ret = OB_ERR_TRUNCATED_WRONG_VALUE;
      if (CM_IS_WARN_ON_FAIL(cast_ctx.cast_mode_)) {
        ret = OB_SUCCESS;
        out = 0;
      }
    }
  } else {
    EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
    EXPR_GET_INT64_V2(obj, out);
  }
  return ret;
}

int ObBitwiseExprOperator::get_uint64(const ObObj& obj, ObExprCtx& expr_ctx, bool is_round, uint64_t& out)
{
  int ret = OB_SUCCESS;
  out = 0;
  ObObjType type = obj.get_type();
  if (OB_LIKELY(ob_is_int_tc(type))) {
    out = static_cast<uint64_t>(obj.get_int());
  } else if (ob_is_uint_tc(type)) {
    out = obj.get_uint64();
  } else if (OB_UNLIKELY(obj.is_number())) {
    uint64_t tmp_uint = 0;
    number::ObNumber value = obj.get_number();
    if (OB_LIKELY(value.is_valid_uint64(tmp_uint))) {
      out = tmp_uint;
    } else {
      int64_t tmp_int = 0;
      number::ObNumber nmb;
      EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
      if (OB_FAIL(nmb.from(value, cast_ctx))) {
        LOG_WARN("deep copy failed", K(ret), K(is_round), K(obj));
      } else if (OB_UNLIKELY(!nmb.is_integer() && OB_FAIL(is_round ? nmb.round(0) : nmb.trunc(0)))) {
        LOG_WARN("round/trunc failed", K(ret), K(is_round), K(nmb));
      } else if (nmb.is_valid_int64(tmp_int)) {
        out = static_cast<uint64_t>(tmp_int);
      } else if (nmb.is_valid_uint64(tmp_uint)) {
        out = tmp_uint;
      } else {
        ret = OB_ERR_TRUNCATED_WRONG_VALUE;
        if (CM_IS_WARN_ON_FAIL(cast_ctx.cast_mode_)) {
          ret = OB_SUCCESS;
          out = 0;
        }
      }
    }
  } else {
    // need add CM_NO_RANGE_CHECK, otherwise 1 & -3.5(float) return 0.
    EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NO_RANGE_CHECK);
    EXPR_GET_UINT64_V2(obj, out);
  }
  return ret;
}

int ObMinMaxExprOperator::assign(const ObExprOperator& other)
{
  int ret = OB_SUCCESS;
  const ObMinMaxExprOperator* tmp_other = dynamic_cast<const ObMinMaxExprOperator*>(&other);
  if (OB_UNLIKELY(NULL == tmp_other)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument. wrong type for other", K(ret), K(other));
  } else if (OB_LIKELY(this != tmp_other)) {
    if (OB_FAIL(ObExprOperator::assign(other))) {
      LOG_WARN("copy in Base class ObExprOperator failed", K(ret));
    } else {
      this->need_cast_ = tmp_other->need_cast_;
    }
  }
  return ret;
}
int ObMinMaxExprOperator::aggregate_result_type_for_comparison(
    ObExprResType& type, const ObExprResType* types, int64_t param_num) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else {
    ObObjType res_type = types[0].get_type();
    ObScale max_scale = types[0].get_scale();
    for (int64_t i = 1; OB_SUCC(ret) && i < param_num; ++i) {
      if (OB_FAIL(ObExprResultTypeUtil::get_relational_result_type(res_type, res_type, types[i].get_type()))) {
        // warn
      } else if (OB_UNLIKELY(ObMaxType == res_type)) {
        ret = OB_INVALID_ARGUMENT;  // not compatible input
      } else if (!ob_is_string_or_lob_type(types[i].get_type()) && types[i].get_scale() > max_scale) {
        max_scale = types[i].get_scale();
      }
    }
    if (OB_SUCC(ret)) {
      type.set_type(res_type);
      type.set_scale(max_scale);
    }
  }
  return ret;
}

int ObMinMaxExprOperator::aggregate_cmp_type_for_comparison(
    ObExprResType& type, const ObExprResType* types, int64_t param_num) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(types) || OB_UNLIKELY(param_num < 1)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("types is null or param_num is wrong", K(types), K(param_num), K(ret));
  } else {
    ObObjType cmp_type = types[0].get_type();
    for (int64_t i = 1; OB_SUCC(ret) && i < param_num; ++i) {
      if (OB_FAIL(ObExprResultTypeUtil::get_relational_cmp_type(cmp_type, cmp_type, types[i].get_type()))) {
        // warn
      } else if (OB_UNLIKELY(ObMaxType == cmp_type)) {
        ret = OB_INVALID_ARGUMENT;  // not compatible input
      }
    }
    if (OB_SUCC(ret)) {
      type.set_calc_type(cmp_type);
    }
  }

  return ret;
}

int ObMinMaxExprOperator::calc_result_meta_for_comparison(ObExprResType& type, ObExprResType* types_stack,
    int64_t param_num, const ObCollationType coll_type, const ObLengthSemantics default_length_semantics) const
{
  UNUSED(default_length_semantics);
  int ret = OB_SUCCESS;
  int64_t i = 0;
  int64_t byte_semantics_str_num = 0;
  int64_t char_semantics_str_num = 0;
  // bool all_string = true;
  if (OB_ISNULL(types_stack) || OB_UNLIKELY(param_num < 1)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("stack is null or param_num is wrong", K(types_stack), K(param_num), K(ret));
  }

  // result_type
  if (OB_SUCC(ret)) {
    ret = aggregate_result_type_for_comparison(type, types_stack, param_num);
  }

  if (OB_SUCC(ret)) {
    ret = aggregate_cmp_type_for_comparison(type, types_stack, param_num);
  }

  bool string_result = ob_is_string_or_enumset_type(type.get_type()) || ob_is_text_tc(type.get_type());
  bool string_cmp = ob_is_string_or_enumset_type(type.get_calc_type()) || ob_is_text_tc(type.get_calc_type());
  // compare collation
  if (OB_SUCC(ret) && string_cmp) {
    ret = aggregate_charsets_for_comparison(type, types_stack, param_num, coll_type);
  }

  // result collation
  if (OB_SUCC(ret) && string_result) {
    ret = aggregate_charsets_for_string_result(type, types_stack, param_num, coll_type);
  }

  if (OB_SUCC(ret)) {
    if (string_result) {
      int64_t max_length = 0;
      for (i = 0; i < param_num; ++i) {
        max_length = MAX(max_length, types_stack[i].get_length());
      }
      type.set_length(static_cast<ObLength>(max_length));
    } else {
      int64_t max_scale = 0;
      int64_t max_precision = 0;
      for (i = 0; i < param_num; ++i) {
        max_scale = MAX(max_scale, types_stack[i].get_mysql_compatible_scale());
        max_precision = MAX(max_precision, types_stack[i].get_precision());
      }
      ObScale result_scale = static_cast<ObScale>(NOT_FIXED_DEC == max_scale ? -1 : max_scale);
      const int64_t int32_max_precision = 11;
      if (result_scale > 0 && ob_is_int_tc(type.get_type())) {
        type.set_type(ObNumberType);
      } else if (max_precision > int32_max_precision && ObInt32Type == type.get_type()) {
        type.set_type(ObIntType);
      }
      type.set_scale(result_scale);
      type.set_precision(static_cast<ObPrecision>(max_precision + max_scale));  // esti, not accurate
    }
  }

  if (OB_SUCC(ret)) {
    ObObjType dest_type = enumset_calc_types_[OBJ_TYPE_TO_CLASS[type.get_calc_type()]];
    if (OB_UNLIKELY(ObMaxType == dest_type)) {
      ret = OB_ERR_UNEXPECTED;
      SQL_ENG_LOG(WARN, "invalid type", K(type), K(ret));
    } else if (ObVarcharType == dest_type) {
      for (int64_t i = 0; OB_SUCC(ret) && i < param_num; ++i) {
        if (ob_is_enumset_tc(types_stack[i].get_type())) {
          types_stack[i].set_calc_type(dest_type);
        }
      }
    } else { /*do nothing*/
    }
  }
  return ret;
}

int ObMinMaxExprOperator::calc_(ObObj& result, const ObObj* objs_stack, int64_t param_num,
    const ObExprResType& result_type, ObExprCtx& expr_ctx, ObCmpOp cmp_op, bool need_cast)
{
  // todo:
  // we assume that result type / result collation / compare type / compare collation are CORRECT.
  // but who knowns? we should check it ASAP.
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(need_cast)) {
    ret = calc_with_cast(result, objs_stack, param_num, result_type, expr_ctx, cmp_op);
  } else {
    ret = calc_without_cast(result, objs_stack, param_num, result_type, expr_ctx, cmp_op);
  }
  return ret;
}

int ObMinMaxExprOperator::calc_without_cast(ObObj& result, const ObObj* objs_stack, int64_t param_num,
    const ObExprResType& result_type, ObExprCtx& expr_ctx, ObCmpOp cmp_op)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(CO_LT != cmp_op && CO_GT != cmp_op)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("the compare oper is wrong", K(ret), K(cmp_op));
  } else if (OB_ISNULL(objs_stack) || OB_ISNULL(expr_ctx.calc_buf_) || OB_UNLIKELY(param_num < 1) ||
             OB_UNLIKELY(result_type.is_invalid())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("stack is null or param_num is wrong", K(objs_stack), K(param_num), K(result_type), K(ret));
  } else {
    bool has_null = false;
    for (int i = 0; OB_SUCC(ret) && !has_null && i < param_num; ++i) {
      if (objs_stack[i].is_null()) {
        has_null = true;
        result.set_null();
      }
    }
    if (!has_null) {
      // compare all params.
      int res_idx = 0;
      ObCollationType cmp_cs_type = result_type.get_calc_collation_type();
      for (int i = 1; OB_SUCC(ret) && i < param_num; ++i) {
        if (ObObjCmpFuncs::compare_oper_nullsafe(objs_stack[i], objs_stack[res_idx], cmp_cs_type, cmp_op)) {
          res_idx = i;
        }
      }
      // ok, we got the least / greatest param.
      if (OB_SUCC(ret)) {
        if (OB_LIKELY(result_type.get_type() == objs_stack[res_idx].get_type() &&
                      result_type.get_collation_type() == objs_stack[res_idx].get_collation_type())) {
          result = objs_stack[res_idx];
        } else {  // slow path
          EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
          if (OB_FAIL(ObObjCaster::to_type(
                  result_type.get_type(), result_type.get_collation_type(), cast_ctx, objs_stack[res_idx], result))) {}
        }
      }
    }
  }
  return ret;
}

int ObMinMaxExprOperator::calc_with_cast(ObObj& result, const ObObj* objs_stack, int64_t param_num,
    const ObExprResType& result_type, ObExprCtx& expr_ctx, ObCmpOp cmp_op)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(CO_LT != cmp_op && CO_GT != cmp_op)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("the compare oper is wrong", K(ret), K(cmp_op));
  } else if (OB_ISNULL(objs_stack) || OB_ISNULL(expr_ctx.calc_buf_) || OB_UNLIKELY(param_num < 1) ||
             OB_UNLIKELY(result_type.is_invalid())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("stack is null or param_num is wrong", K(objs_stack), K(param_num), K(result_type), K(ret));
  } else {
    EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
    if (ob_is_nstring_type(result_type.get_calc_type())) {
      cast_ctx.dest_collation_ = expr_ctx.my_session_->get_nls_collation_nation();
    } else if (share::is_mysql_mode() && CS_TYPE_INVALID != result_type.get_collation_type()) {
      cast_ctx.dest_collation_ = result_type.get_collation_type();
    }
    ObFixedArray<ObObj, ObIAllocator> buf_obj(expr_ctx.calc_buf_, param_num);
    ObFixedArray<const ObObj*, ObIAllocator> res_obj(expr_ctx.calc_buf_, param_num);  // inited in the for loop below.
    if (OB_FAIL(buf_obj.prepare_allocate(param_num))) {
      LOG_WARN("prepare allocate failed", K(param_num), K(ret));
    } else if (OB_FAIL(res_obj.prepare_allocate(param_num))) {
      LOG_WARN("prepare allocate failed", K(param_num), K(ret));
    }
    // ret status will be checked within OB_SUCC(ret)s
    bool has_null = false;
    // cast all params to cmp type, and check if exist null.
    for (int i = 0; OB_SUCC(ret) && !has_null && i < param_num; ++i) {
      res_obj[i] = NULL;
      if (objs_stack[i].is_null()) {
        has_null = true;
        result.set_null();
      } else if (OB_FAIL(ObObjCaster::to_type(
                     result_type.get_calc_type(), cast_ctx, objs_stack[i], buf_obj[i], res_obj[i]))) {

      } else if (OB_ISNULL(res_obj[i])) {
        ret = OB_ERR_UNEXPECTED;
      }
    }
    if (OB_SUCC(ret) && !has_null) {
      // compare all params.
      int res_idx = 0;
      ObCollationType cmp_cs_type = result_type.get_calc_collation_type();
      for (int i = 1; OB_SUCC(ret) && i < param_num; ++i) {
        if (ObObjCmpFuncs::compare_oper_nullsafe(*res_obj[i], *res_obj[res_idx], cmp_cs_type, cmp_op)) {
          res_idx = i;
        }
      }
      // ok, we got the least / greatest param.
      if (OB_SUCC(ret)) {
        if (OB_FAIL(ObObjCaster::to_type(
                result_type.get_type(), result_type.get_collation_type(), cast_ctx, *res_obj[res_idx], result))) {}
      }
    }
  }
  return ret;
}

int ObArithExprOperator::interval_add_minus(
    ObObj& res, const ObObj& left, const ObObj& right, ObExprCtx& expr_ctx, ObScale scale, bool is_minus)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(ObIntervalTC != right.get_type_class())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret));
  } else {
    switch (left.get_type_class()) {
      case ObIntervalTC: {  // interval + interval
        if (OB_UNLIKELY(left.get_type() != right.get_type())) {
          ret = OB_ERR_INVALID_TYPE_FOR_OP;  // ORA-30081: invalid data type for datetime/interval arithmetic
          LOG_WARN("invalid data type interval arithmetic");
        } else if (left.is_interval_ym()) {
          ObIntervalYMValue value = is_minus ? left.get_interval_ym() - right.get_interval_ym()
                                             : left.get_interval_ym() + right.get_interval_ym();
          if (OB_FAIL(value.validate())) {
            LOG_WARN("value validate failed", K(ret), K(value));
          } else {
            res.set_interval_ym(value);
            res.set_scale(ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalYMType].get_scale());
          }
          LOG_DEBUG("add interval year to month result", K(value), K(res));
        } else {
          ObIntervalDSValue value = is_minus ? left.get_interval_ds() - right.get_interval_ds()
                                             : left.get_interval_ds() + right.get_interval_ds();
          if (OB_FAIL(value.validate())) {
            LOG_WARN("value validate failed", K(ret), K(value));
          } else {
            res.set_interval_ds(value);
            res.set_scale(ObAccuracy::MAX_ACCURACY2[ORACLE_MODE][ObIntervalDSType].get_scale());
          }
          LOG_DEBUG("add interval day to second result", K(value), K(res));
        }
        break;
      }
      case ObDateTimeTC: {  // date +- interval
        int64_t date_v = left.get_datetime();
        int64_t result_v = 0;
        int64_t sign = is_minus ? -1 : 1;

        if (OB_FAIL(right.is_interval_ym() ? ObTimeConverter::date_add_nmonth(
                                                 date_v, right.get_interval_ym().get_nmonth() * sign, result_v)
                                           : ObTimeConverter::date_add_nsecond(date_v,
                                                 right.get_interval_ds().get_nsecond() * sign,
                                                 right.get_interval_ds().get_fs() * sign,
                                                 result_v))) {
          LOG_WARN("add value failed", K(ret), K(left), K(right));
        } else {
          res.set_datetime(result_v);
          res.set_scale(0);
        }
        break;
      }
      case ObOTimestampTC: {  // timestamp +- interval
        ObOTimestampData result_v;
        int32_t sign = is_minus ? -1 : 1;

        if (OB_FAIL(right.is_interval_ym() ? ObTimeConverter::otimestamp_add_nmonth(left.get_type(),
                                                 left.get_otimestamp_value(),
                                                 get_timezone_info(expr_ctx.my_session_),
                                                 right.get_interval_ym().get_nmonth() * sign,
                                                 result_v)
                                           : ObTimeConverter::otimestamp_add_nsecond(left.get_otimestamp_value(),
                                                 right.get_interval_ds().get_nsecond() * sign,
                                                 right.get_interval_ds().get_fs() * sign,
                                                 result_v))) {
          LOG_WARN("calc with timestamp value failed", K(ret), K(left), K(right));
        } else {
          res.set_otimestamp_value(left.get_type(), result_v);
          res.set_scale(MAX_SCALE_FOR_ORACLE_TEMPORAL);
        }
        break;
      }
      case ObNullTC: {
        res.set_null();
        break;
      }
      default: {
        ret = OB_ERR_INVALID_TYPE_FOR_OP;  // ORA-30081: invalid data type for datetime/interval arithmetic
        LOG_WARN("invalid calc type", K(ret));
      }
    }
  }
  LOG_DEBUG("add interval", K(left), K(right), K(scale), K(res));
  UNUSED(scale);
  return ret;
}

int ObMinMaxExprOperator::deserialize(const char* buf, const int64_t data_len, int64_t& pos)
{
  int ret = OB_SUCCESS;
  need_cast_ = true;  // defensive code
  if (OB_FAIL(ObExprOperator::deserialize(buf, data_len, pos))) {
    LOG_WARN("deserialize in BASE class failed", K(ret));
  } else {
    OB_UNIS_DECODE(need_cast_);
  }
  return ret;
}

int ObMinMaxExprOperator::serialize(char* buf, const int64_t buf_len, int64_t& pos) const
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(ObExprOperator::serialize(buf, buf_len, pos))) {
    LOG_WARN("serialize in BASE class failed", K(ret));
  } else {
    OB_UNIS_ENCODE(need_cast_);
  }
  return ret;
}

int64_t ObMinMaxExprOperator::get_serialize_size() const
{
  int64_t len = 0;
  BASE_ADD_LEN((ObMinMaxExprOperator, ObExprOperator));
  OB_UNIS_ADD_LEN(need_cast_);
  return len;
}

// ObLocationExprOperator

int ObLocationExprOperator::calc_result_type2(
    ObExprResType& type, ObExprResType& type1, ObExprResType& type2, ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  ObExprOperator::calc_result_flag2(type, type1, type2);
  type.set_int();
  type.set_precision(ObAccuracy::DDL_DEFAULT_ACCURACY[ObIntType].precision_);
  type.set_scale(DEFAULT_SCALE_FOR_INTEGER);
  type1.set_calc_type(ObVarcharType);
  type2.set_calc_type(ObVarcharType);
  type_ctx.set_cast_mode(type_ctx.get_cast_mode() | CM_STRING_INTEGER_TRUNC);
  ObObjMeta types[2] = {type1, type2};
  OZ(aggregate_charsets_for_comparison(type.get_calc_meta(), types, 2, type_ctx.get_coll_type()));
  OX(type1.set_calc_collation_type(type.get_calc_collation_type()));
  OX(type2.set_calc_collation_type(type.get_calc_collation_type()));
  return ret;
}

int ObLocationExprOperator::calc_result2(
    common::ObObj& result, const common::ObObj& obj1, const common::ObObj& obj2, common::ObExprCtx& expr_ctx) const
{
  ObObj position;
  position.set_int(1);
  return ObLocationExprOperator::calc_result3(result, obj1, obj2, position, expr_ctx);
}

int ObLocationExprOperator::calc_result3(common::ObObj& result, const common::ObObj& obj1, const common::ObObj& obj2,
    const common::ObObj& obj3, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("the pointer is null", K(expr_ctx.calc_buf_), K(ret));
  } else if (OB_UNLIKELY(obj1.is_null() || obj2.is_null())) {
    result.set_null();
  } else if (obj3.is_null()) {
    result.set_int(0);
  } else if (OB_UNLIKELY(!is_type_valid(obj1.get_type()) || !is_type_valid(obj2.get_type()) ||
                         !is_type_valid(obj3.get_type()))) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("the param is not castable", K(obj1), K(obj2), K(obj3), K(ret));
  } else {
    int64_t pos = 0;
    ret = get_pos_int64(obj3, expr_ctx, pos);
    if (OB_SUCC(ret)) {
      TYPE_CHECK(obj1, ObVarcharType);
      TYPE_CHECK(obj2, ObVarcharType);
      ObString str1 = obj1.get_string();
      ObString str2 = obj2.get_string();
      uint32_t idx = ObCharset::locate(
          result_type_.get_calc_collation_type(), str2.ptr(), str2.length(), str1.ptr(), str1.length(), pos);
      result.set_int(static_cast<int64_t>(idx));
    }
  }
  return ret;
}

int ObLocationExprOperator::get_pos_int64(const ObObj& obj, ObExprCtx& expr_ctx, int64_t& out)
{
  int ret = OB_SUCCESS;
  out = 0;
  ObObjType type = obj.get_type();
  if (OB_LIKELY(ob_is_int_tc(type))) {
    out = obj.get_int();
  } else if (ob_is_uint_tc(type)) {
    uint64_t value = obj.get_uint64();
    out = (value > INT64_MAX) ? 0 : static_cast<int64_t>(value);
  } else if (OB_UNLIKELY(obj.is_number())) {
    int64_t tmp_int = 0;
    uint64_t tmp_uint = 0;
    number::ObNumber nmb = obj.get_number();
    number::ObNumber* pnmb = &nmb;
    number::ObNumber newmb;
    if (OB_UNLIKELY(!nmb.is_integer())) {
      // such as select locate('bar', 'foobarbar', 5.3); yeah, really ugly.
      EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
      if (OB_FAIL(newmb.from(nmb, cast_ctx))) {  // copy is essential since if we did not do that, obj will be modified
        LOG_WARN("copy nmb failed", K(ret), K(nmb));
      } else if (OB_FAIL(newmb.round(0))) {
        LOG_WARN("round failed", K(ret), K(nmb));
      } else {
        pnmb = &newmb;
      }
    }
    if (OB_FAIL(ret)) {
      // do nothing
    } else if (OB_ISNULL(pnmb)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("unexpected error. null pointer", K(ret));
    } else if (pnmb->is_valid_int64(tmp_int)) {
      out = tmp_int;
    } else if (pnmb->is_valid_uint64(tmp_uint)) {
      out = 0;  // no errors  no warnings in mysql.
    } else {
      ret = OB_ERR_TRUNCATED_WRONG_VALUE;
      if (CM_IS_WARN_ON_FAIL(expr_ctx.cast_mode_)) {
        ret = OB_SUCCESS;
        out = 0;
      }
    }
  } else {
    EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
    EXPR_GET_INT64_V2(obj, out);
  }
  return ret;
}

int ObLocationExprOperator::get_calc_cs_type(const ObExpr& expr, ObCollationType& calc_cs_type)
{
  int ret = OB_SUCCESS;
  const ObCollationType cs_type1 = expr.args_[0]->datum_meta_.cs_type_;
  const ObCollationType cs_type2 = expr.args_[1]->datum_meta_.cs_type_;
  if (OB_UNLIKELY(cs_type1 != cs_type2)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("cs type should be same", K(ret), K(cs_type1), K(cs_type2));
  } else if (OB_UNLIKELY(!ObCharset::is_valid_collation(static_cast<int64_t>(cs_type1)))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid cs_type", K(ret), K(cs_type1));
  } else {
    calc_cs_type = cs_type1;
  }
  return ret;
}

int ObLocationExprOperator::calc_location_expr(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& res_datum)
{
  int ret = OB_SUCCESS;
  // locate(sub, ori, pos)
  if (OB_UNLIKELY(2 > expr.arg_cnt_ || 3 < expr.arg_cnt_) || OB_ISNULL(expr.args_) || OB_ISNULL(expr.args_[0]) ||
      OB_ISNULL(expr.args_[1])) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid expr", K(ret), K(expr));
  } else if (OB_FAIL(calc_(expr, *expr.args_[0], *expr.args_[1], ctx, res_datum))) {
    LOG_WARN("calc_ faied", K(ret));
  }
  return ret;
}

int ObLocationExprOperator::calc_(
    const ObExpr& expr, const ObExpr& sub_arg, const ObExpr& ori_arg, ObEvalCtx& ctx, ObDatum& res_datum)
{
  int ret = OB_SUCCESS;
  ObDatum* sub = NULL;
  ObDatum* ori = NULL;
  ObDatum* pos = NULL;
  bool has_result = false;
  if (OB_FAIL(sub_arg.eval(ctx, sub)) || OB_FAIL(ori_arg.eval(ctx, ori))) {
    LOG_WARN("eval arg failed", K(ret));
  } else if (sub->is_null() || ori->is_null()) {
    res_datum.set_null();
    has_result = true;
  }

  int64_t pos_int = 1;
  if (OB_SUCC(ret) && !has_result && 3 == expr.arg_cnt_) {
    if (OB_FAIL(expr.args_[2]->eval(ctx, pos))) {
      LOG_WARN("eval arg 2 failed", K(ret));
    } else if (pos->is_null()) {
      res_datum.set_int(0);
      has_result = true;
    } else {
      pos_int = pos->get_int();
    }
  }

  if (OB_SUCC(ret) && !has_result) {
    const ObString& ori_str = ori->get_string();
    const ObString& sub_str = sub->get_string();
    ObCollationType calc_cs_type = CS_TYPE_INVALID;
    if (OB_FAIL(get_calc_cs_type(expr, calc_cs_type))) {
      LOG_WARN("get_calc_cs_type failed", K(ret));
    } else {
      uint32_t idx =
          ObCharset::locate(calc_cs_type, ori_str.ptr(), ori_str.length(), sub_str.ptr(), sub_str.length(), pos_int);
      res_datum.set_int(static_cast<int64_t>(idx));
    }
  }
  return ret;
}

int ObLocationExprOperator::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  rt_expr.eval_func_ = calc_location_expr;
  return OB_SUCCESS;
}

int ObExprTRDateFormat::calc_hash(const char* p, int64_t len, uint64_t& hash)
{
  int ret = OB_SUCCESS;
  hash = 0;
  if (OB_ISNULL(p) || OB_UNLIKELY(len <= 0)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_ERROR("unexpected error.invalid arguments", K(p), K(len));
  } else {
    for (int64_t i = 0; i < len; ++i) {
      hash = (hash << 7) + (hash << 1) + hash + toupper(p[i]);
    }
  }
  return ret;
}

int ObExprTRDateFormat::init()
{
  int ret = OB_SUCCESS;
  for (int64_t i = 0; i < FORMAT_MAX_TYPE && OB_SUCC(ret); ++i) {
    ret = calc_hash(FORMATS_TEXT[i], strlen(FORMATS_TEXT[i]), FORMATS_HASH[i]);
    // validation
    for (int64_t j = 0; j < i && OB_SUCC(ret); ++j) {
      if (FORMATS_HASH[i] == FORMATS_HASH[j]) {
        ret = OB_ERR_UNEXPECTED;
        LOG_ERROR("unexpected error.hash func is not perfect hash", K(i), K(j));
        break;
      }
    }
  }
  return ret;
}

int ObExprTRDateFormat::trunc_new_obtime(ObTime& ob_time, const ObString& fmt)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(fmt.empty())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("empty string.", K(ret), K(fmt));
  } else {
    int64_t fmt_id = SYYYY;
    const char* ptr = fmt.ptr();
    int32_t ptr_len = fmt.length();
    uint64_t fmt_hash = 0;
    if (OB_FAIL(calc_hash(ptr, ptr_len, fmt_hash))) {
      LOG_WARN("calc hash failed", K(ret), K(fmt));
    } else if (FALSE_IT(get_format_id(fmt_hash, fmt_id))) {
    } else if (OB_UNLIKELY(fmt_id < 0 || fmt_id >= FORMAT_MAX_TYPE)) {
      ret = OB_INVALID_DATE_FORMAT;
      LOG_WARN("invalid format string", K(ret), K(fmt_id), K(fmt));
    } else if (OB_UNLIKELY(strncasecmp(ptr, FORMATS_TEXT[fmt_id], ptr_len))) {
      // what a pity ! same hash value, while not expected content
      ret = OB_INVALID_DATE_FORMAT;
      LOG_WARN("invalid format string", K(ret), K(fmt_id), K(fmt));
    } else {
      switch (fmt_id) {
        case SYYYY:
          // go through
        case YYYY:
          // go through
        case YEAR:
        case SYEAR:
          // go through
        case YYY:
          // go through
        case YY:
          // go through
        case Y: {
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_YDAY] - 1);
          ob_time.parts_[DT_DATE] -= offset;
          break;
        }
        case Q: {
          set_time_part_to_zero(ob_time);
          int32_t quarter = (ob_time.parts_[DT_MON] + 2) / MONS_PER_QUAR;
          ob_time.parts_[DT_MON] = (quarter - 1) * MONS_PER_QUAR + 1;
          ob_time.parts_[DT_MDAY] = 1;
          ob_time.parts_[DT_DATE] = ObTimeConverter::ob_time_to_date(ob_time);
          break;
        }
        case MONTH:
          // go through
        case MON:
          // go through
        case MM:
          // go through
        case RM: {
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_MDAY] - 1);
          ob_time.parts_[DT_DATE] -= offset;
          break;
        }
        case WW: {
          // 01-01 is the first day of year
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_YDAY] - 1) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] -= offset;
          break;
        }
        case IW: {
          // within a week. monday is the first day
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_WDAY] - 1) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] -= offset;
          break;
        }
        case W: {
          // xx-01 is the first day
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_MDAY] - 1) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] -= offset;
          break;
        }
        case DDD:
          // go through
        case DD:
          // go through
        case J: {
          set_time_part_to_zero(ob_time);
          break;
        }
        case DAY:
          // go through
        case DY:
          // go through
        case D: {
          // within a week. sunday is the first day
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_WDAY]) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] -= offset;
          break;
        }
        case HH:
          // go through
        case HH12:
          // go through
        case HH24: {
          ob_time.parts_[DT_MIN] = 0;
          ob_time.parts_[DT_SEC] = 0;
          ob_time.parts_[DT_USEC] = 0;
          break;
        }
        case MI: {
          ob_time.parts_[DT_SEC] = 0;
          ob_time.parts_[DT_USEC] = 0;
          break;
        }
        case CC:
          // go through
        case SCC: {
          set_time_part_to_zero(ob_time);
          ob_time.parts_[DT_YEAR] = ob_time.parts_[DT_YEAR] / YEARS_PER_CENTURY * YEARS_PER_CENTURY + 1;
          ob_time.parts_[DT_MON] = 1;
          ob_time.parts_[DT_MDAY] = 1;
          ob_time.parts_[DT_DATE] = ObTimeConverter::ob_time_to_date(ob_time);
          break;
        }
        case IYYY:
          // go through
        case IY:
          // go through
        case I: {
          // not used heavily. so, do not care too much about performance !
          set_time_part_to_zero(ob_time);
          ObTimeConverter::get_first_day_of_isoyear(ob_time);
          break;
        }
        default: {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("unexpected fmt_id", K(fmt_id), K(ret), K(fmt));
        }
      }  // end switch
    }
  }
  return ret;
}

int ObExprTRDateFormat::round_new_obtime(ObTime& ob_time, const ObString& fmt)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(fmt.empty())) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("empty string.", K(ret), K(fmt));
  } else {
    int64_t fmt_id = SYYYY;
    const char* ptr = fmt.ptr();
    int32_t ptr_len = fmt.length();
    uint64_t fmt_hash = 0;
    if (OB_FAIL(calc_hash(ptr, ptr_len, fmt_hash))) {
      LOG_WARN("calc hash failed", K(ret), K(fmt));
    } else if (FALSE_IT(get_format_id(fmt_hash, fmt_id))) {
    } else if (OB_UNLIKELY(fmt_id < 0 || fmt_id >= FORMAT_MAX_TYPE)) {
      ret = OB_INVALID_DATE_FORMAT;
      LOG_WARN("invalid format string", K(ret), K(fmt_id), K(fmt));
    } else if (OB_UNLIKELY(strncasecmp(ptr, FORMATS_TEXT[fmt_id], ptr_len))) {
      // what a pity ! same hash value, while not expected content
      ret = OB_INVALID_DATE_FORMAT;
      LOG_WARN("invalid format string", K(ret), K(fmt_id), K(fmt));
    } else {
      LOG_DEBUG("check value", K(ob_time), K(fmt_id), K(fmt));
      switch (fmt_id) {
        case SYYYY:
          // go through
        case YYYY:
          // go through
        case YEAR:
        case SYEAR:
          // go through
        case YYY:
          // go through
        case YY:
          // go through
        case Y: {
          //>6
          const int32_t add_year = (ob_time.parts_[DT_MON] > DT_PART_MAX[DT_MON] / 2) ? 1 : 0;
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_YDAY] - 1);
          ob_time.parts_[DT_DATE] =
              ob_time.parts_[DT_DATE] - offset + add_year * DAYS_PER_YEAR[IS_LEAP_YEAR(ob_time.parts_[DT_YEAR])];
          break;
        }
        case Q: {
          //>15
          const int32_t add_quarter = (((ob_time.parts_[DT_MON] - 1) % MONS_PER_QUAR > 1) ||
                                          (1 == (ob_time.parts_[DT_MON] - 1) % MONS_PER_QUAR &&
                                              ob_time.parts_[DT_MDAY] > DT_PART_MAX[DT_MDAY] / 2))
                                          ? 1
                                          : 0;
          set_time_part_to_zero(ob_time);
          int32_t quarter = (ob_time.parts_[DT_MON] + 2) / MONS_PER_QUAR + add_quarter;
          ob_time.parts_[DT_MON] = (quarter - 1) * MONS_PER_QUAR + 1;
          if (ob_time.parts_[DT_MON] > DT_PART_MAX[DT_MON]) {
            ob_time.parts_[DT_MON] -= static_cast<int32_t>(DT_PART_MAX[DT_MON]);
            ob_time.parts_[DT_YEAR] += 1;
          }
          ob_time.parts_[DT_MDAY] = 1;
          ob_time.parts_[DT_DATE] = ObTimeConverter::ob_time_to_date(ob_time);
          break;
        }
        case MONTH:
          // go through
        case MON:
          // go through
        case MM:
          // go through
        case RM: {
          //>15
          const int32_t add_month = (ob_time.parts_[DT_MDAY] > DT_PART_MAX[DT_MDAY] / 2) ? 1 : 0;
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_MDAY] - 1);
          ob_time.parts_[DT_DATE] =
              ob_time.parts_[DT_DATE] - offset +
              add_month * DAYS_PER_MON[IS_LEAP_YEAR(ob_time.parts_[DT_YEAR])][ob_time.parts_[DT_MON]];
          break;
        }
        case WW: {
          // 01-01 is the first day of year
          const int32_t add_ww = ((((ob_time.parts_[DT_YDAY] - 1) % DAYS_PER_WEEK) > DAYS_PER_WEEK / 2) ||
                                     (((ob_time.parts_[DT_YDAY] - 1) % DAYS_PER_WEEK) == DAYS_PER_WEEK / 2 &&
                                         ob_time.parts_[DT_HOUR] > DT_PART_MAX[DT_HOUR] / 2))
                                     ? 1
                                     : 0;
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_YDAY] - 1) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] = ob_time.parts_[DT_DATE] - offset + add_ww * DAYS_PER_WEEK;
          break;
        }
        case IW: {
          // within a week. monday is the first day
          const int32_t add_iw = ((((ob_time.parts_[DT_WDAY] - 1) % DAYS_PER_WEEK) > DAYS_PER_WEEK / 2) ||
                                     (((ob_time.parts_[DT_WDAY] - 1) % DAYS_PER_WEEK) == DAYS_PER_WEEK / 2 &&
                                         ob_time.parts_[DT_HOUR] > DT_PART_MAX[DT_HOUR] / 2))
                                     ? 1
                                     : 0;
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_WDAY] - 1) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] = ob_time.parts_[DT_DATE] - offset + add_iw * DAYS_PER_WEEK;
          break;
        }
        case W: {
          // xx-01 is the first day
          const int32_t add_w = ((((ob_time.parts_[DT_MDAY] - 1) % DAYS_PER_WEEK) > DAYS_PER_WEEK / 2) ||
                                    (((ob_time.parts_[DT_MDAY] - 1) % DAYS_PER_WEEK) == DAYS_PER_WEEK / 2 &&
                                        ob_time.parts_[DT_HOUR] > DT_PART_MAX[DT_HOUR] / 2))
                                    ? 1
                                    : 0;
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_MDAY] - 1) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] = ob_time.parts_[DT_DATE] - offset + add_w * DAYS_PER_WEEK;
          break;
        }
        case DDD:
          // go through
        case DD:
          // go through
        case J: {
          const int32_t add_d = (ob_time.parts_[DT_HOUR] > DT_PART_MAX[DT_HOUR] / 2) ? 1 : 0;
          set_time_part_to_zero(ob_time);
          ob_time.parts_[DT_DATE] += add_d;
          break;
        }
        case DAY:
          // go through
        case DY:
          // go through
        case D: {
          // within a week. sunday is the first day
          const int32_t add_dy = (((ob_time.parts_[DT_WDAY] % DAYS_PER_WEEK) > DAYS_PER_WEEK / 2) ||
                                     ((ob_time.parts_[DT_WDAY] % DAYS_PER_WEEK) == DAYS_PER_WEEK / 2 &&
                                         ob_time.parts_[DT_HOUR] > DT_PART_MAX[DT_HOUR] / 2))
                                     ? 1
                                     : 0;
          set_time_part_to_zero(ob_time);
          int32_t offset = (ob_time.parts_[DT_WDAY]) % DAYS_PER_WEEK;
          ob_time.parts_[DT_DATE] = ob_time.parts_[DT_DATE] - offset + add_dy * DAYS_PER_WEEK;
          break;
        }
        case HH:
          // go through
        case HH12:
          // go through
        case HH24: {
          const int32_t add_dh = (ob_time.parts_[DT_MIN] > DT_PART_MAX[DT_MIN] / 2) ? 1 : 0;
          ob_time.parts_[DT_MIN] = 0;
          ob_time.parts_[DT_SEC] = 0;
          ob_time.parts_[DT_USEC] = 0;
          ob_time.parts_[DT_HOUR] += add_dh;
          if (ob_time.parts_[DT_HOUR] > DT_PART_MAX[DT_HOUR]) {
            ob_time.parts_[DT_HOUR] -= static_cast<int32_t>(DT_PART_MAX[DT_HOUR]);
            ob_time.parts_[DT_DATE] += 1;
          }
          break;
        }
        case MI: {
          const int32_t add_mi = (ob_time.parts_[DT_SEC] >= DT_PART_MAX[DT_SEC] / 2) ? 1 : 0;
          ob_time.parts_[DT_SEC] = 0;
          ob_time.parts_[DT_USEC] = 0;
          ob_time.parts_[DT_MIN] += add_mi;
          if (ob_time.parts_[DT_MIN] > DT_PART_MAX[DT_MIN]) {
            ob_time.parts_[DT_MIN] -= static_cast<int32_t>(DT_PART_MAX[DT_MIN]);
            ob_time.parts_[DT_HOUR] += 1;

            if (ob_time.parts_[DT_HOUR] > DT_PART_MAX[DT_HOUR]) {
              ob_time.parts_[DT_HOUR] -= static_cast<int32_t>(DT_PART_MAX[DT_HOUR]);
              ob_time.parts_[DT_DATE] += 1;
            }
          }
          break;
        }
        case CC:
          // go through
        case SCC: {
          const int32_t add_cc = (ob_time.parts_[DT_YEAR] % YEARS_PER_CENTURY > YEARS_PER_CENTURY / 2) ? 1 : 0;
          set_time_part_to_zero(ob_time);
          ob_time.parts_[DT_YEAR] =
              ob_time.parts_[DT_YEAR] / YEARS_PER_CENTURY * YEARS_PER_CENTURY + 1 + add_cc * YEARS_PER_CENTURY;
          ob_time.parts_[DT_MON] = 1;
          ob_time.parts_[DT_MDAY] = 1;
          ob_time.parts_[DT_DATE] = ObTimeConverter::ob_time_to_date(ob_time);
          break;
        }
        case IYYY:
          // go through
        case IY:
          // go through
        case I: {
          // not used heavily. so, do not care too much about performance !
          set_time_part_to_zero(ob_time);
          ret = ObTimeConverter::get_round_day_of_isoyear(ob_time);
          break;
        }
        default: {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("unexpected fmt_id", K(fmt_id), K(ret), K(fmt));
        }
      }  // end switch
    }
  }
  return ret;
}

int ObRelationalExprOperator::is_row_cmp(const ObRawExpr& raw_expr, int& row_dim)
{
  int ret = OB_SUCCESS;
  row_dim = -1;
  if (OB_UNLIKELY(2 != raw_expr.get_param_count())) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid param count", K(ret), K(raw_expr.get_param_count()));
  } else if (T_OP_ROW == raw_expr.get_param_expr(0)->get_expr_type() &&
             T_OP_ROW == raw_expr.get_param_expr(1)->get_expr_type()) {
    if (raw_expr.get_param_expr(0)->get_param_count() != raw_expr.get_param_expr(1)->get_param_count()) {
      if (1 == raw_expr.get_param_expr(1)->get_param_count() &&
          T_OP_ROW == raw_expr.get_param_expr(1)->get_param_expr(0)->get_expr_type()) {
        // (c1, c2) = (c1, c2), (c1, c2) = ((c1, c2)) are both allowed in oralce mode
        if (raw_expr.get_param_expr(0)->get_param_count() ==
            raw_expr.get_param_expr(1)->get_param_expr(0)->get_param_count()) {
          row_dim = raw_expr.get_param_expr(0)->get_param_count();
        }
      }
    } else {
      row_dim = raw_expr.get_param_expr(0)->get_param_count();
    }
    if (OB_UNLIKELY(-1 == row_dim)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("unexpected param cnt",
          K(raw_expr.get_param_expr(0)->get_param_count()),
          K(raw_expr.get_param_expr(1)->get_param_count()),
          K(raw_expr.get_param_expr(1)->get_expr_type()));
    }
  }
  return ret;
}

int ObRelationalExprOperator::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  int ret = OB_SUCCESS;
  int row_dim = -1;
  if (OB_FAIL(is_row_cmp(raw_expr, row_dim))) {
    LOG_WARN("failed to get row dimension", K(ret));
  } else if (OB_ISNULL(op_cg_ctx.allocator_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid null allocator", K(ret), K(op_cg_ctx.allocator_));
  } else if (row_dim > 0) {
    ret = cg_row_cmp_expr(row_dim, *op_cg_ctx.allocator_, raw_expr, input_types_, rt_expr);
  } else {
    ret = cg_datum_cmp_expr(raw_expr, input_types_, rt_expr);
  }
  return ret;
}

int ObRelationalExprOperator::cg_datum_cmp_expr(
    const ObRawExpr& raw_expr, const ObExprOperatorInputTypeArray& input_types, ObExpr& rt_expr)
{
  int ret = OB_SUCCESS;
  UNUSED(raw_expr);
  if (OB_UNLIKELY(2 != rt_expr.arg_cnt_ || NULL == rt_expr.args_ || NULL == rt_expr.args_[0] ||
                  NULL == rt_expr.args_[1] || input_types.count() != 2)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret));
  } else {
    rt_expr.inner_func_cnt_ = 0;
    rt_expr.inner_functions_ = NULL;

    const ObCmpOp cmp_op = get_cmp_op(raw_expr.get_expr_type());
    const ObObjType input_type1 = rt_expr.args_[0]->datum_meta_.type_;
    const ObObjType input_type2 = rt_expr.args_[1]->datum_meta_.type_;
    LOG_DEBUG("CG Datum CMP Expr", K(input_type1), K(input_type2), K(cmp_op));
    const ObCollationType cs_type = rt_expr.args_[0]->datum_meta_.cs_type_;
    if (ObDatumFuncs::is_string_type(input_type1) && ObDatumFuncs::is_string_type(input_type2)) {
      CK(rt_expr.args_[0]->datum_meta_.cs_type_ == rt_expr.args_[1]->datum_meta_.cs_type_);
      rt_expr.eval_func_ = ObExprCmpFuncsHelper::get_eval_expr_cmp_func(
          input_type1, input_type2, cmp_op, lib::is_oracle_mode(), cs_type);
    } else {
      rt_expr.eval_func_ = ObExprCmpFuncsHelper::get_eval_expr_cmp_func(
          input_type1, input_type2, cmp_op, lib::is_oracle_mode(), cs_type);
      CK(NULL != rt_expr.eval_func_);
    }
  }
  return ret;
}

int ObRelationalExprOperator::cg_row_cmp_expr(const int row_dimension, ObIAllocator& allocator,
    const ObRawExpr& raw_expr, const ObExprOperatorInputTypeArray& input_types, ObExpr& rt_expr)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(rt_expr.arg_cnt_ != 2 || row_dimension <= 0 || NULL == rt_expr.args_ || NULL == rt_expr.args_[0] ||
                  NULL == rt_expr.args_[1] || rt_expr.args_[0]->arg_cnt_ != row_dimension ||
                  input_types.count() != row_dimension * 2)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret));
  } else {
    void** inner_func_buf = NULL;
    if (OB_ISNULL(inner_func_buf = (void**)allocator.alloc(sizeof(void*) * row_dimension))) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
    } else {
      rt_expr.inner_func_cnt_ = row_dimension;
      rt_expr.inner_functions_ = inner_func_buf;

      const ObCmpOp cmp_op = get_cmp_op(raw_expr.get_expr_type());

      ObExpr* left_row = rt_expr.args_[0];
      ObExpr* right_row = NULL;
      if (OB_LIKELY(T_OP_ROW == rt_expr.args_[1]->type_ && NULL != rt_expr.args_[1]->args_[0])) {
        if (T_OP_ROW == rt_expr.args_[1]->args_[0]->type_) {
          right_row = rt_expr.args_[1]->args_[0];
        } else {
          right_row = rt_expr.args_[1];
        }
      } else {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("unexpected error", K(ret));
      }
      if (OB_ISNULL(right_row) || OB_ISNULL(left_row)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("right_row or left_row is null ptr", K(ret), K(right_row), K(left_row));
      } else if (OB_UNLIKELY(left_row->arg_cnt_ != right_row->arg_cnt_)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("unexpected row cnt", K(left_row->arg_cnt_), K(right_row->arg_cnt_));
      }
      LOG_DEBUG("CG ROW CMP Expr", K(input_types), K(cmp_op));

      for (int i = 0; OB_SUCC(ret) && i < row_dimension; i++) {
        const ObObjType type1 = left_row->args_[i]->datum_meta_.type_;
        const ObObjType type2 = right_row->args_[i]->datum_meta_.type_;
        const ObCollationType cs_type = left_row->args_[i]->datum_meta_.cs_type_;
        if (ObDatumFuncs::is_string_type(type1) && ObDatumFuncs::is_string_type(type2)) {
          CK(left_row->args_[i]->datum_meta_.cs_type_ == right_row->args_[i]->datum_meta_.cs_type_);
          rt_expr.inner_functions_[i] =
              (void*)ObExprCmpFuncsHelper::get_datum_expr_cmp_func(type1, type2, lib::is_oracle_mode(), cs_type);
        } else {
          rt_expr.inner_functions_[i] =
              (void*)ObExprCmpFuncsHelper::get_datum_expr_cmp_func(type1, type2, lib::is_oracle_mode(), cs_type);
          if (OB_ISNULL(rt_expr.inner_functions_[i])) {
            ret = OB_ERR_UNEXPECTED;
            LOG_WARN("unexpected null function", K(ret), K(i), K(type1), K(type2));
          }
        }
      }  // for end
      if (OB_SUCC(ret)) {
        rt_expr.eval_func_ = &row_eval;
      }
    }
  }
  return ret;
}

int ObRelationalExprOperator::row_eval(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(2 != expr.arg_cnt_ || NULL == expr.args_ || expr.inner_func_cnt_ <= 0 ||
                  expr.args_[0]->arg_cnt_ != expr.inner_func_cnt_ || NULL == expr.args_[0]->args_ ||
                  NULL == expr.args_[1]->args_ || NULL == expr.inner_functions_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret));
  } else {
    ObExpr* left_row = expr.args_[0];
    ObExpr* right_row = NULL;
    if (1 == expr.args_[1]->arg_cnt_ && T_OP_ROW == expr.args_[1]->args_[0]->type_) {
      if (expr.args_[1]->args_[0]->arg_cnt_ != expr.inner_func_cnt_) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("unexpected arg cnt", K(ret), K(expr.inner_func_cnt_), K(expr.args_[1]->args_[0]->arg_cnt_));
      } else {
        right_row = expr.args_[1]->args_[0];
      }
    } else if (OB_UNLIKELY(expr.inner_func_cnt_ != expr.args_[1]->arg_cnt_)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("unexpected arg cnt", K(ret), K(expr.inner_func_cnt_), K(expr.args_[1]->arg_cnt_));
    } else {
      right_row = expr.args_[1];
    }
    ret = row_cmp(expr, expr_datum, left_row->args_, ctx, right_row->args_, ctx);
  }
  return ret;
}

int ObRelationalExprOperator::row_cmp(
    const ObExpr& expr, ObDatum& expr_datum, ObExpr** l_row, ObEvalCtx& l_ctx, ObExpr** r_row, ObEvalCtx& r_ctx)
{
  // performance critical, do not check pointer validity.
  int ret = OB_SUCCESS;
  ObDatum* left = NULL;
  ObDatum* right = NULL;

  bool cnt_row_null = false;
  int first_nonequal_cmp_ret = 0;
  int i = 0;
  // locate first non-equal pair
  for (; OB_SUCC(ret) && i < expr.inner_func_cnt_; i++) {
    if (OB_FAIL(l_row[i]->eval(l_ctx, left))) {
      LOG_WARN("failed to eval left in row cmp", K(ret));
    } else if (left->is_null()) {
      cnt_row_null = true;
    } else if (OB_FAIL(r_row[i]->eval(r_ctx, right))) {
      LOG_WARN("failed to eval right in row cmp", K(ret));
    } else if (right->is_null()) {
      cnt_row_null = true;
    } else if (0 != (first_nonequal_cmp_ret = ((DatumCmpFunc)expr.inner_functions_[i])(*left, *right))) {
      break;
    }
  }  // for end
  ObCmpOp cmp_op = get_cmp_op(expr.type_);
  if (OB_FAIL(ret)) {
    // do nothing
  } else if (i == expr.inner_func_cnt_) {
    if (cnt_row_null) {
      expr_datum.set_null();
    } else {
      expr_datum.set_int(is_expected_cmp_ret(cmp_op, 0));
    }
  } else {
    if (cnt_row_null) {
      if (CO_NE == cmp_op) {
        expr_datum.set_int(true);
      } else if (CO_EQ == cmp_op) {
        expr_datum.set_int(false);
      } else {
        expr_datum.set_null();
      }
    } else {
      expr_datum.set_int(is_expected_cmp_ret(cmp_op, first_nonequal_cmp_ret));
    }
  }
  return ret;
}

}  // namespace sql
}  // namespace oceanbase
