/**
 * 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 <string.h>
#include "sql/parser/ob_item_type.h"
#include "sql/engine/expr/ob_expr_inet.h"
#include "sql/engine/ob_exec_context.h"

using namespace oceanbase::common;

namespace oceanbase {
namespace sql {
int ObExprInetCommon::str_to_ipv4(int len, const char *str, bool& is_ip_format_invalid, in_addr* ipv4addr)
{
  is_ip_format_invalid = false;
  int ret = OB_SUCCESS;
  //Shortest IPv4 address："x.x.x.x"，length:7
  if (7 > len || INET_ADDRSTRLEN - 1 < len) {
    is_ip_format_invalid = true;
    LOG_WARN("ip format invalid, too short or too long", K(len));
  } else if (OB_UNLIKELY(OB_UNLIKELY(OB_ISNULL(str) || OB_ISNULL(ipv4addr)))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("ip_str or ipv4addr is null", K(ret), K(str), K(ipv4addr));
  } else {
    unsigned char byte_addr[4];
    int dotcnt = 0, numcnt = 0;
    int byte = 0;
    char c;
    for (int i = 0; !is_ip_format_invalid && i < len && *(i + str); ++i) {
      c = *(i + str);
      if ('.' == c) {
        if (255 < byte) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid", K(byte));
        } else if (0 == numcnt || 3 < numcnt) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, group num count invalid", K(numcnt), K(dotcnt));
        } else if (i == len - 1 || !*(i + str + 1)) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, end with '.'", K(i));
        } else {
          byte_addr[dotcnt] = (unsigned char) byte;
        }
        dotcnt ++;
        if (3 < dotcnt) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, too much '.'", K(dotcnt), K(i));
        } else {
        }
        byte = 0;
        numcnt = 0;
      } else if ('9' >= c && '0' <= c) {
        byte = byte * 10 + c - '0';
        numcnt ++;
      } else {
        is_ip_format_invalid = true;
        LOG_WARN("ip format invalid, invalid number", K(c), K(i));
      }
    }
    if (255 < byte) {
      is_ip_format_invalid = true;
      LOG_WARN("ip format invalid", K(byte));
    } else if (3 != dotcnt) {
      is_ip_format_invalid = true;
      LOG_WARN("ip format invalid, number of '.' is not 3", K(dotcnt));
    } else if ('.' == c) { // IP number can't end on '.'
      is_ip_format_invalid = true;
      LOG_WARN("ip format invalid, end with '.'");
    } else {
      byte_addr[3] = (unsigned char) byte;
    }
    MEMCPY((unsigned char *) ipv4addr, byte_addr, sizeof(in_addr));
  }
  return ret;
}

int ObExprInetCommon::str_to_ipv6(int len, const char *str, bool& is_ip_format_invalid, in6_addr* ipv6addr)
{
  int ret = OB_SUCCESS;
  is_ip_format_invalid = false;
  //Ipv6 length of mysql support: 2~39
  //Shortest IPv6 address："::"，length:2
  if (2 > len || INET6_ADDRSTRLEN - 1 < len) {
    is_ip_format_invalid = true;
    LOG_WARN("ip format invalid, too short or too long", K(len));
  } else if (OB_UNLIKELY(OB_ISNULL(str) || OB_ISNULL(ipv6addr))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("ip_str or ipv6addr is null", K(ret), K(str), K(ipv6addr));
  } else {
    int dst_index = 0;
    char c;
    int group = 0, num = 0;
    int numcnt = 0;
    int group_start = 0;
    int gap_index = -1;
    int i = 0;
    uint8_t ip_addr[sizeof(in6_addr)];
    MEMSET(ip_addr, 0, sizeof(in6_addr));
    if (':' == str[0]) {
      if (':' == str[1]) {
        i = 1;
      } else { //cannot start with ":x"
        is_ip_format_invalid = true;
        LOG_WARN("ip format invalid, start with :x", K(str[1]));
      }
    } else {
    }
    for (; !is_ip_format_invalid && i < len && str[i]; ++i) {
      c = str[i];
      if (':' == c) {
        group_start = i + 1;
        if (0 == numcnt) {
          if (-1 == gap_index) {
            gap_index = dst_index;
          } else { //number of "::" greater than 1
            is_ip_format_invalid = true;
            LOG_WARN("ip format invalid, too much '::'", K(gap_index), K(i));
          }
        } else if (i >= len || !str[i+1]) { // cannot end with "x:"
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, end with x:", K(i));
        } else if (dst_index + 2 > sizeof(in6_addr)) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid", K(i), K(dst_index));
        } else {
          ip_addr[dst_index] = (unsigned char) (group >> 8) & 0xff;
          ip_addr[dst_index + 1] = (unsigned char) group & 0xff;
          dst_index += 2;
          group = 0;
          numcnt = 0;
        }
      } else if ('.' == c) {
        if (dst_index + sizeof(in_addr) > sizeof(in6_addr)) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, no room for ipv4", K(dst_index), K(i));
        } else if (OB_FAIL(str_to_ipv4(len - group_start, str + group_start, is_ip_format_invalid, (in_addr *)(ip_addr + dst_index)))) {
          LOG_WARN("fail to excute str_to_ipv4", K(ret));
        } else if (is_ip_format_invalid) {
          LOG_WARN("ipv4 format invalid", K(group_start));
        } else {
          numcnt = 0;
          dst_index += 4;
        }
        break;
      } else {
        numcnt++;
        if (4 < numcnt) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, too many numbers in one group", K(numcnt), K(i));
        } else {
          if ('0' <= c && '9' >= c) {
            num = c - '0';
          } else if ('a' <= c && 'f' >= c) {
            num = c - 'a' + 10;
          } else if ('A' <= c && 'F' >= c) {
            num = c - 'A' + 10;
          } else {
            is_ip_format_invalid = true;
            LOG_WARN("ip format invalid, invaild number", K(c), K(i));
          }
          group = (group << 4) | num;
        }
      }
    }
    if (!is_ip_format_invalid) {
      if (0 != numcnt) { //add last group: ":xxxx"
        if (dst_index + 2 > sizeof(in6_addr)) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, too many groups", K(dst_index), K(i));
        } else {
          ip_addr[dst_index] = (unsigned char) (group >> 8) & 0xff;
          ip_addr[dst_index+1] = (unsigned char) group & 0xff;
          dst_index += 2;
        }
      } else {
      }
      if (-1 != gap_index) {
        if (dst_index == sizeof(in6_addr)) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid, no room for ::", K(gap_index));
        } else {
          int to_move = dst_index - gap_index;
          for (int i = 1; i <= to_move; ++i) {
            ip_addr[sizeof(in6_addr) - i] = ip_addr[gap_index + to_move - i];
            ip_addr[gap_index + to_move - i] = 0;
          }
        }
      } else if (dst_index != sizeof(in6_addr)) {
        is_ip_format_invalid = true;
        LOG_WARN("ip format invalid", K(dst_index),K(sizeof(in6_addr)));
      } else {
      }
      MEMCPY(ipv6addr, ip_addr, sizeof(in6_addr));
    }
  }
  return ret;
}

int ObExprInetCommon::ip_to_str(ObString& ip_binary, bool& is_ip_format_invalid, ObString& ip_str)
{
  is_ip_format_invalid = false;
  int ret = OB_SUCCESS;
  const char *ip = ip_binary.ptr();
  char * result_ptr = ip_str.ptr();
  char result[common::MAX_IP_ADDR_LENGTH];
  if (OB_UNLIKELY(OB_ISNULL(ip) || OB_ISNULL(result_ptr))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("ip or result is null", K(ret), K(ip), K(result));    
  }else if (sizeof(in_addr) == ip_binary.length()) {
    const unsigned char *ipv4_groups = (const unsigned char *) ip;
    sprintf((char *)result_ptr, "%d.%d.%d.%d", ipv4_groups[0], ipv4_groups[1], ipv4_groups[2], ipv4_groups[3]);
    ip_str.assign(result_ptr, static_cast<ObString::obstr_size_t>(strlen(result_ptr)));
  } else if (sizeof(in6_addr) == ip_binary.length()) {
    int gap = -1, gap_len = -1;
    int rg = -1, rg_len = -1;
    const uint16_t *ipv6_binary = (const uint16_t *) ip;
    uint16_t ipv6_groups[8];
    for (int i= 0; i < 8; ++i) {
      ipv6_groups[i] = (ipv6_binary[i] << 8) | ((ipv6_binary[i] >> 8) & 0xff);
    }
    //find longest gap
    for (int i = 0; i < 8; ++i) {
      if (0 == ipv6_groups[i]) {
        if (-1 == rg_len) {
          rg_len = 1;
          rg = i;
        } else {
          rg_len++;
        }
      } else {
        if (rg_len > gap_len) {
          gap = rg;
          gap_len = rg_len;
        } else {
        }
        rg_len = -1;
      }
    }
    if (rg_len > gap_len) {
      gap = rg;
      gap_len = rg_len;
    } else {
    }
    char *p = &result[0];
    for (int i = 0; i < 8; ++i) {
      if (gap == i) {
        if (0 == i) {
          *p = ':';
          ++p;
        } else {
        }
        *p = ':';
        ++p;
        i += gap_len - 1;  //skip gap
      } else if (6 == i && 0 == gap &&
             (6 == gap_len ||                           // IPv4-compatible
              (5 == gap_len && 0xffff == ipv6_groups[5]) // IPv4-mapped
             ))
      {
        const unsigned char *ipv4_groups = (const unsigned char *) ip + 12;
        p += sprintf(p, "%d.%d.%d.%d", ipv4_groups[0], ipv4_groups[1], ipv4_groups[2], ipv4_groups[3]);
        break;
      } else {
        p += sprintf(p, "%x", ipv6_groups[i]);
        if (i != 7) {
          *p = ':';
          ++p;
        }
      }
    }
    *p = '\0';
    int len = static_cast<ObString::obstr_size_t>(strlen(result));
    MEMCPY(result_ptr, result, len);
    ip_str.assign(result_ptr, len);
  } else {
    is_ip_format_invalid = true;
    LOG_WARN("invalid ip length", K(ip_binary.length()));
  }
  return ret;
}

ObExprInetAton::ObExprInetAton(ObIAllocator& alloc)
    : ObFuncExprOperator(alloc, T_FUN_SYS_INETATON, N_INETATON, 1, NOT_ROW_DIMENSION)
{}

ObExprInetAton::~ObExprInetAton()
{}

int ObExprInetAton::calc(ObObj& result, const ObObj& text, uint64_t cast_mode)
{
  int ret = OB_SUCCESS;
  if (text.is_null()) {
    result.set_null();
  } else {
    ObString m_text = text.get_string();
    bool is_ip_format_invalid = false;
    if (OB_FAIL(ob_inet_aton(result, m_text, is_ip_format_invalid))) {
      LOG_WARN("fail to excute ob_inet_aton", K(ret));
    } else if (is_ip_format_invalid) {
      if (CM_IS_WARN_ON_FAIL(cast_mode)) {
        result.set_null();
      } else {
        ret = OB_INVALID_ARGUMENT;
        LOG_WARN("ip format invalid", K(ret), K(m_text));
      }
    }
  }
  return ret;
}

int ObExprInetAton::calc_result1(ObObj& result, const ObObj& text, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  uint64_t cast_mode = 0;
  ObSQLSessionInfo* session = expr_ctx.my_session_;
  if (OB_UNLIKELY(OB_ISNULL(session))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("session is NULL", K(ret));
  } else {
    ObSQLUtils::get_default_cast_mode(session->get_stmt_type(), session, cast_mode);
    if (OB_FAIL(calc(result, text, cast_mode))) {
      LOG_WARN("fail to calc", K(ret));
    } else if (OB_LIKELY(!result.is_null())) {
      result.set_collation_type(result_type_.get_collation_type());
      result.set_collation_level(result_type_.get_collation_level());
    } else {
    }
  }
  return ret;
}

template <typename T>
int ObExprInetAton::ob_inet_aton(T& result, const ObString& text, bool& is_ip_format_invalid)
{
  int ret = OB_SUCCESS;
  is_ip_format_invalid = false;
  if (text.empty()) {
    is_ip_format_invalid = true;
    LOG_WARN("ip length is zero");
  } else {
    int len = text.length();
    char c;
    int cnt = 0;
    unsigned long value = 0;
    int byte = 0;
    for (int i = 0; !is_ip_format_invalid && i < len; ++i) {
      c = text.ptr()[i];
      if ('0' <= c && '9' >= c) {
        byte = byte * 10 + c - '0';
        if (255 < byte) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid", K(byte), K(text));
        } else {
        }
      } else if ('.' == c) {
        cnt++;
        value = (value << 8) + byte;
        byte = 0;
      } else {
        is_ip_format_invalid = true;
        LOG_WARN("ip format invalid", K(c), K(text));
      }
    }
    if (is_ip_format_invalid) {
    } else if (3 < cnt || '.' == c) { // IP number can't end on '.', '.' count <= 3
      is_ip_format_invalid = true;
      LOG_WARN("ip format invalid", K(text), K(cnt), K(c));
    } else {
      /*
      Attempt to support short forms of IP-addresses.
      Examples:
        127     -> 0.0.0.127
        127.255 -> 127.0.0.255
        127.256 -> NULL
        127.2.1 -> 127.2.0.1
      */
      if (2 == cnt) {
        value <<= 8;
      } else if (1 == cnt) {
        value <<= 16;
      } else {
      }
      value = (value << 8) + byte;
      result.set_int(value);
    }
  }
  return ret;
}

int ObExprInetAton::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  int ret = OB_SUCCESS;
  if (rt_expr.arg_cnt_ != 1) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("inet_aton expr should have one param", K(ret), K(rt_expr.arg_cnt_));
  } else if (OB_UNLIKELY(OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0]))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("children of inet_aton expr is null", K(ret), K(rt_expr.args_));
  } else {
    rt_expr.eval_func_ = ObExprInetAton::calc_inet_aton;
  }
  return ret;
}

int ObExprInetAton::calc_inet_aton(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(expr.eval_param_value(ctx))) {
    LOG_WARN("inet_aton expr eval param value failed", K(ret));
  } else {
    ObDatum& text = expr.locate_param_datum(ctx, 0);
    if (text.is_null()) {
      expr_datum.set_null();
    } else {
      ObString m_text = text.get_string();
      bool is_ip_format_invalid = false;
      if (OB_FAIL(ob_inet_aton(expr_datum, m_text, is_ip_format_invalid))) {
        LOG_WARN("fail to excute ob_inet_aton", K(ret));
      }else if (is_ip_format_invalid) {
        uint64_t cast_mode = 0;
        ObSQLSessionInfo* session = ctx.exec_ctx_.get_my_session();
        if (OB_UNLIKELY(OB_ISNULL(session))) {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("session is NULL", K(ret));
        } else {
          ObSQLUtils::get_default_cast_mode(session->get_stmt_type(), session, cast_mode);
          if (CM_IS_WARN_ON_FAIL(cast_mode)) { //support no strict sql_mode
            expr_datum.set_null();
          } else {
            ret = OB_INVALID_ARGUMENT;
            LOG_WARN("fail to convert ip to int", K(ret), K(m_text));
          }
        }
      }
    }
  }
  return ret;
}

ObExprInet6Ntoa::ObExprInet6Ntoa(ObIAllocator& alloc) : ObStringExprOperator(alloc, T_FUN_SYS_INET6NTOA, N_INET6NTOA, 1)
{}

ObExprInet6Ntoa::~ObExprInet6Ntoa()
{}

inline int ObExprInet6Ntoa::calc_result_type1(
    ObExprResType& type, ObExprResType& text, common::ObExprTypeCtx& type_ctx) const
{
  int ret = OB_SUCCESS;
  UNUSED(text);
  type.set_varchar();
  type.set_length(common::MAX_IP_ADDR_LENGTH);
  type.set_collation_level(common::CS_LEVEL_COERCIBLE);
  const sql::ObSQLSessionInfo *session = type_ctx.get_session();
  if (OB_UNLIKELY(OB_ISNULL(session))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("session is null",K(ret));
  } else {
    type.set_collation_type(get_default_collation_type(type.get_type(), *type_ctx.get_session()));
  }
  return ret;
}

int ObExprInet6Ntoa::calc(ObObj& result, const ObObj& text, common::ObExprStringBuf& string_buf, uint64_t cast_mode)
{
  int ret = OB_SUCCESS;
  bool is_ip_format_invalid = false;
  if (text.is_null()) {
    result.set_null();
  } else if (!text.is_varbinary()) {
    is_ip_format_invalid = true;
    LOG_WARN("ip is not binary", K(ret), K(text));
  } else {
    ObString num_val = text.get_varbinary();
    char *buf = static_cast<char*>(string_buf.alloc(MAX_IP_ADDR_LENGTH));
    if (OB_ISNULL(buf)) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_ERROR("alloc memory failed", K(ret), K(text));
    } else {
      ObString ip_str(MAX_IP_ADDR_LENGTH, 0, buf);
      ObExprInetCommon::ip_to_str(num_val, is_ip_format_invalid, ip_str);
      result.set_varchar(ip_str);
    }
  }
  if (OB_SUCC(ret) && is_ip_format_invalid) {
    if (CM_IS_WARN_ON_FAIL(cast_mode)) {
      result.set_null();
    } else {
      ret = OB_INVALID_ARGUMENT;
      LOG_WARN("ip format invalid", K(ret), K(text));
    }
  }
  return ret;
}

int ObExprInet6Ntoa::calc_result1(ObObj& result, const ObObj& text, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  uint64_t cast_mode = 0;
  ObSQLSessionInfo* session = expr_ctx.my_session_;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("varchar buffer not init", K(ret));
  } else if (OB_UNLIKELY(OB_ISNULL(session))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("session is NULL", K(ret));
  } else {
    ObSQLUtils::get_default_cast_mode(session->get_stmt_type(), session, cast_mode);
    if (OB_FAIL(calc(result, text, *expr_ctx.calc_buf_, cast_mode))) {
      LOG_WARN("fail to calc", K(ret));
    } else if (OB_LIKELY(!result.is_null())) {
      result.set_collation_type(result_type_.get_collation_type());
      result.set_collation_level(result_type_.get_collation_level());
    } else {
    }
  }
  return ret;
}

int ObExprInet6Ntoa::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  int ret = OB_SUCCESS;
  if (1 != rt_expr.arg_cnt_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("inet6_ntoa expr should have one param", K(ret), K(rt_expr.arg_cnt_));
  } else if (OB_UNLIKELY(OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0]))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("children of inet6_ntoa expr is null", K(ret), K(rt_expr.args_));
  } else {
    rt_expr.eval_func_ = ObExprInet6Ntoa::calc_inet6_ntoa;
  }
  return ret;
}

int ObExprInet6Ntoa::calc_inet6_ntoa(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(expr.eval_param_value(ctx))) {
    LOG_WARN("inet6_ntoa expr eval param value failed", K(ret));
  } else {
    ObDatum& text = expr.locate_param_datum(ctx, 0);
    if (text.is_null()) {
      expr_datum.set_null();
    } else {
      char * buf = NULL;
      CK(expr.res_buf_len_ >= MAX_IP_ADDR_LENGTH);
      if (OB_FAIL(ret)) {
        LOG_WARN("result buf size greater than MAX_IP_ADDR_LENGTH", K(ret));
      } else if (OB_ISNULL(buf = expr.get_str_res_mem(ctx, MAX_IP_ADDR_LENGTH))) {
        ret = OB_ALLOCATE_MEMORY_FAILED;
        LOG_WARN("Failed to allocate memory for lob locator", K(ret), K(MAX_IP_ADDR_LENGTH));
      } else {
        bool is_ip_format_invalid = false;
        ObString num_val = text.get_string();
        ObString ip_str(MAX_IP_ADDR_LENGTH, 0, buf);
        if (!ob_is_varbinary_type(expr.args_[0]->datum_meta_.type_,expr.args_[0]->datum_meta_.cs_type_)) {
          is_ip_format_invalid = true;
          LOG_WARN("ip format invalid", K(ret), K(text));
        } else if (OB_FAIL(ObExprInetCommon::ip_to_str(num_val, is_ip_format_invalid, ip_str))) {
          LOG_WARN("fail to excute ip_to_str", K(ret));
        } else if (!is_ip_format_invalid) {
          expr_datum.set_string(ip_str);
        }
        if (OB_SUCC(ret) && is_ip_format_invalid) {
          uint64_t cast_mode = 0;
          ObSQLSessionInfo* session = ctx.exec_ctx_.get_my_session();
          if (OB_UNLIKELY(OB_ISNULL(session))) {
            ret = OB_ERR_UNEXPECTED;
            LOG_WARN("session is NULL", K(ret));
          } else {
            ObSQLUtils::get_default_cast_mode(session->get_stmt_type(), session, cast_mode);
            if (CM_IS_WARN_ON_FAIL(cast_mode)) {
              expr_datum.set_null(); //support no strict sql_mode
            } else {
              ret = OB_INVALID_ARGUMENT;
              LOG_WARN("ip format invalid", K(ret),K(text));
            }
          }
        }
      }
    }
  }
  return ret;
}

ObExprInet6Aton::ObExprInet6Aton(ObIAllocator& alloc)
    : ObFuncExprOperator(alloc, T_FUN_SYS_INET6ATON, N_INET6ATON, 1, NOT_ROW_DIMENSION)
{}

ObExprInet6Aton::~ObExprInet6Aton()
{}

int ObExprInet6Aton::calc(ObObj& result, const ObObj& text, common::ObExprStringBuf& string_buf, uint64_t cast_mode)
{
  int ret = OB_SUCCESS;
  if (text.is_null()) {
    result.set_null();
  } else {
    ObString num_val = text.get_varbinary();
    char *buf = static_cast<char*>(string_buf.alloc(sizeof(in6_addr)));
    if (OB_ISNULL(buf)) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_ERROR("alloc memory failed", K(ret), K(text));
    } else {
      bool is_ip_format_invalid = false;
      ObString m_text = text.get_string();
      ObString result_buf(sizeof(in6_addr), 0, buf);
      if (OB_FAIL(inet6_aton(m_text, is_ip_format_invalid, result_buf))) {
        LOG_WARN("fail to excute inet6_aton");
      } else if (is_ip_format_invalid) {
        if (CM_IS_WARN_ON_FAIL(cast_mode)) {
          result.set_null(); //support no strict mode
        } else {
          ret = OB_INVALID_ARGUMENT;
          LOG_WARN("ip format invalid", K(ret), K(m_text));
        }
      } else {
        result.set_varbinary(result_buf);
      }
    }
  }
  return ret;
}

int ObExprInet6Aton::calc_result1(ObObj& result, const ObObj& text, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  uint64_t cast_mode = 0;
  ObSQLSessionInfo* session = expr_ctx.my_session_;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("varchar buffer not init", K(ret));
  } else if (OB_UNLIKELY(OB_ISNULL(session))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("session is NULL", K(ret));
  } else {
    ObSQLUtils::get_default_cast_mode(session->get_stmt_type(), session, cast_mode);
    if (OB_FAIL(calc(result, text, *expr_ctx.calc_buf_, cast_mode))) {
      LOG_WARN("fail to calc", K(ret));
    } else if (OB_LIKELY(!result.is_null())) {
      result.set_collation_type(result_type_.get_collation_type());
      result.set_collation_level(result_type_.get_collation_level());
    } else {
    }
  }
  return ret;
}

int ObExprInet6Aton::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  int ret = OB_SUCCESS;
  if (1 != rt_expr.arg_cnt_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("inet6_aton expr should have one param", K(ret), K(rt_expr.arg_cnt_));
  } else if (OB_UNLIKELY(OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0]))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("children of inet6_aton expr is null", K(ret), K(rt_expr.args_));
  } else {
    rt_expr.eval_func_ = ObExprInet6Aton::calc_inet6_aton;
  }
  return ret;
}

int ObExprInet6Aton::calc_inet6_aton(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(expr.eval_param_value(ctx))) {
    LOG_WARN("inet6_ntoa expr eval param value failed", K(ret));
  } else {
    char * buf = NULL;
    CK(expr.res_buf_len_ >= sizeof(in6_addr));
    if (OB_FAIL(ret)) {
      LOG_WARN("result buf size greater than sizeof(in6_addr)", K(ret));
    } else if (OB_ISNULL(buf = expr.get_str_res_mem(ctx, sizeof(in6_addr)))) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_WARN("Failed to allocate memory for lob locator", K(ret), K(sizeof(in6_addr)));
    } else {
      ObDatum& text = expr.locate_param_datum(ctx, 0);
      ObString m_text = text.get_string();
      bool is_ip_format_invalid = false;
      if (text.is_null()) {
        expr_datum.set_null();
      } else {
        ObString str_result(sizeof(in6_addr), 0, buf);
        if (OB_FAIL(inet6_aton(m_text, is_ip_format_invalid, str_result))) {
          LOG_WARN("fail to excute inet6_aton", K(ret));
        } else if (is_ip_format_invalid) {
          uint64_t cast_mode = 0;
          ObSQLSessionInfo* session = ctx.exec_ctx_.get_my_session();
          if (OB_ISNULL(session)) {
            LOG_WARN("session is NULL", K(ret));
          } else {
            ObSQLUtils::get_default_cast_mode(session->get_stmt_type(), session, cast_mode);
            if (CM_IS_WARN_ON_FAIL(cast_mode)) {
              expr_datum.set_null(); //support no strict mode
            } else {
              ret = OB_INVALID_ARGUMENT;
              LOG_WARN("ip format invalid", K(ret));
            }
          }
        } else {
          expr_datum.set_string(str_result);
        }
      }
    }
  }
  return ret;
}

int ObExprInet6Aton::inet6_aton(const ObString& ip, bool& is_ip_format_invalid, ObString& str_result)
{
  is_ip_format_invalid = false;
  int ret = OB_SUCCESS;
  char buf[MAX_IP_ADDR_LENGTH];
  if (INET6_ADDRSTRLEN - 1 < ip.length()) {
    is_ip_format_invalid = true;
    LOG_WARN("ip format invalid", K(ip));
  } else {
    char *result_buf = str_result.ptr();
    if (OB_UNLIKELY(OB_ISNULL(result_buf))) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("result_buf is null", K(ret));
    } else {
      MEMCPY(buf, ip.ptr(), ip.length());
      buf[ip.length()] = '\0';
      if (OB_FAIL(ObExprInetCommon::str_to_ipv4(ip.length(), buf, is_ip_format_invalid, (in_addr *)result_buf))) {
        LOG_WARN("fail to excute str_to_ipv4", K(ret));
      }else if (is_ip_format_invalid) {
        if (OB_FAIL(ObExprInetCommon::str_to_ipv6(ip.length(), buf, is_ip_format_invalid, (in6_addr *)result_buf))) {
          LOG_WARN("fail to excute str_to_ipv6", K(ret));
        } else if (is_ip_format_invalid) {
          LOG_WARN("ip format invalid", K(ip));
        } else {
          str_result.assign(result_buf, sizeof(in6_addr));
        }
      } else {
        str_result.assign(result_buf, sizeof(in_addr));
      }
    }
  }
  return ret;
}

ObExprIsIpv4::ObExprIsIpv4(ObIAllocator& alloc)
    : ObFuncExprOperator(alloc, T_FUN_SYS_IS_IPV4, N_IS_IPV4, 1, NOT_ROW_DIMENSION)
{}

ObExprIsIpv4::~ObExprIsIpv4()
{}

int ObExprIsIpv4::calc(ObObj& result, const ObObj& text)
{
  int ret = OB_SUCCESS;
  if (text.is_null()) {
    result.set_int(0);
  } else {
    ObString m_text = text.get_string();
    if (OB_FAIL(is_ipv4(result, m_text))) {
      LOG_WARN("fail to excute is_ipv4", K(ret));
    }
  }
  return ret;
}

int ObExprIsIpv4::calc_result1(ObObj& result, const ObObj& text, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("varchar buffer not init", K(ret));
  } else if (OB_FAIL(calc(result, text))) {
    LOG_WARN("fail to excute calc", K(ret));
  }
  return ret;
}

template <typename T>
int ObExprIsIpv4::is_ipv4(T& result, const ObString& text)
{
  char buf[16];
  int ipv4_ret = 1;
  int ret = OB_SUCCESS;
  if (INET_ADDRSTRLEN - 1 < text.length()) {
    ipv4_ret = 0;
  } else {
    MEMCPY(buf, text.ptr(), text.length());
    int len = text.length();
    buf[len] = '\0';
    struct in_addr addr;
    bool is_ip_invalid;
    if (OB_FAIL(ObExprInetCommon::str_to_ipv4(len, buf, is_ip_invalid, &addr))) {
      LOG_WARN("fail to excute str_to_ipv4");
    } else {
      ipv4_ret = is_ip_invalid ? 0 : 1;
    }
  }
  result.set_int(ipv4_ret);
  return ret;
}

int ObExprIsIpv4::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  int ret = OB_SUCCESS;
  if (1 != rt_expr.arg_cnt_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("is_ipv4 expr should have one param", K(ret), K(rt_expr.arg_cnt_));
  } else if (OB_UNLIKELY(OB_UNLIKELY(OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0])))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("children of is_ipv4 expr is null", K(ret), K(rt_expr.args_));
  } else {
    rt_expr.eval_func_ = ObExprIsIpv4::calc_is_ipv4;
  }
  return ret;
}

int ObExprIsIpv4::calc_is_ipv4(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(expr.eval_param_value(ctx))) {
    LOG_WARN("is_ipv4 expr eval param value failed", K(ret));
  } else {
    ObDatum& text = expr.locate_param_datum(ctx, 0);
    ObString m_text = text.get_string();
    if (text.is_null()) {
      expr_datum.set_int(0);
    } else if (OB_FAIL(is_ipv4(expr_datum, m_text))) {
      LOG_WARN("fail to excute is_ipv4", K(ret));
    } else {
    }
  }
  return ret;
}

ObExprIsIpv4Mapped::ObExprIsIpv4Mapped(ObIAllocator& alloc)
    : ObFuncExprOperator(alloc, T_FUN_SYS_IS_IPV4_MAPPED, N_IS_IPV4_MAPPED, 1, NOT_ROW_DIMENSION)
{}

ObExprIsIpv4Mapped::~ObExprIsIpv4Mapped()
{}

void ObExprIsIpv4Mapped::calc(ObObj& result, const ObObj& text)
{
  if (text.is_null()) {
    result.set_int(0);
  } else if (!text.is_varbinary()) {
    result.set_int(0);
  } else {
    ObString m_text = text.get_varbinary();
    is_ipv4_mapped(result, m_text);
  }
}

int ObExprIsIpv4Mapped::calc_result1(ObObj& result, const ObObj& text, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("varchar buffer not init", K(ret));
  } else {
    calc(result, text);
  }
  return ret;
}

int ObExprIsIpv4Mapped::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  int ret = OB_SUCCESS;
  if (1 != rt_expr.arg_cnt_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("is_ipv4_mapped expr should have one param", K(ret), K(rt_expr.arg_cnt_));
  } else if (OB_UNLIKELY(OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0]))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("children of is_ipv4_mapped expr is null", K(ret), K(rt_expr.args_));
  } else {
    rt_expr.eval_func_ = ObExprIsIpv4Mapped::calc_is_ipv4_mapped;
  }
  return ret;
}

int ObExprIsIpv4Mapped::calc_is_ipv4_mapped(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(expr.eval_param_value(ctx))) {
    LOG_WARN("is_ipv4_mapped expr eval param value failed", K(ret));
  } else {
    ObDatum& text = expr.locate_param_datum(ctx, 0);
    if (text.is_null()) {
      expr_datum.set_int(0);
    } else if (!ob_is_varbinary_type(expr.args_[0]->datum_meta_.type_,expr.args_[0]->datum_meta_.cs_type_)) {
      expr_datum.set_int(0);
    } else {
      ObString m_text = text.get_string();
      is_ipv4_mapped(expr_datum, m_text);
    }
  }
  return ret;
}

template <typename T>
void ObExprIsIpv4Mapped::is_ipv4_mapped(T& result, const ObString& num_val)
{
  char buf[16];
  int ipv4_ret = 1;
  if (sizeof(in6_addr) != num_val.length()) { //length of binary ipv6 addr is 16
    ipv4_ret = 0;
  } else {
    MEMCPY(buf, num_val.ptr(), num_val.length());
    ipv4_ret = IN6_IS_ADDR_V4MAPPED((struct in6_addr *) buf);
  }
  result.set_int(ipv4_ret);
}

ObExprIsIpv4Compat::ObExprIsIpv4Compat(ObIAllocator& alloc)
    : ObFuncExprOperator(alloc, T_FUN_SYS_IS_IPV4_COMPAT, N_IS_IPV4_COMPAT, 1, NOT_ROW_DIMENSION)
{}

ObExprIsIpv4Compat::~ObExprIsIpv4Compat()
{}

void ObExprIsIpv4Compat::calc(ObObj& result, const ObObj& text)
{
  if (text.is_null()) {
    result.set_int(0);
  } else if (!text.is_varbinary()) {
    result.set_int(0);
  } else {
    ObString m_text = text.get_varbinary();
    is_ipv4_compat(result, m_text);
  }
}

int ObExprIsIpv4Compat::calc_result1(ObObj& result, const ObObj& text, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("varchar buffer not init", K(ret));
  } else {
    calc(result, text);
  }
  return ret;
}

int ObExprIsIpv4Compat::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  int ret = OB_SUCCESS;
  if (1 != rt_expr.arg_cnt_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("is_ipv4_compat expr should have one param", K(ret), K(rt_expr.arg_cnt_));
  } else if (OB_UNLIKELY(OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0]))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("children of is_ipv4_compat expr is null", K(ret), K(rt_expr.args_));
  } else {
    rt_expr.eval_func_ = ObExprIsIpv4Compat::calc_is_ipv4_compat;
  }
  return ret;
}

int ObExprIsIpv4Compat::calc_is_ipv4_compat(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(expr.eval_param_value(ctx))) {
    LOG_WARN("is_ipv4_compat expr eval param value failed", K(ret));
  } else {
    ObDatum& text = expr.locate_param_datum(ctx, 0);
    if (text.is_null()) {
      expr_datum.set_int(0);
    } else if (!ob_is_varbinary_type(expr.args_[0]->datum_meta_.type_, expr.args_[0]->datum_meta_.cs_type_)) {
      expr_datum.set_int(0);
    } else {
      ObString m_text = text.get_string();
      is_ipv4_compat(expr_datum, m_text);
    }
  }
  return ret;
}

template <typename T>
void ObExprIsIpv4Compat::is_ipv4_compat(T& result, const ObString& num_val)
{
  char buf[16];
  int ipv4_ret = 1;
  if (sizeof(in6_addr) != num_val.length()) { //length of binary ipv6 addr is 16
    ipv4_ret = 0;
  } else {
    MEMCPY(buf, num_val.ptr(), num_val.length());
    ipv4_ret = IN6_IS_ADDR_V4COMPAT((struct in6_addr *) buf);
  }
  result.set_int(ipv4_ret);
}

ObExprIsIpv6::ObExprIsIpv6(ObIAllocator& alloc)
    : ObFuncExprOperator(alloc, T_FUN_SYS_IS_IPV6, N_IS_IPV6, 1, NOT_ROW_DIMENSION)
{}

ObExprIsIpv6::~ObExprIsIpv6()
{}

int ObExprIsIpv6::calc(ObObj& result, const ObObj& text)
{
  int ret = OB_SUCCESS;
  if (text.is_null()) {
    result.set_int(0);
  } else {
    ObString m_text = text.get_string();
    if (OB_FAIL(is_ipv6(result, m_text))) {
      LOG_WARN("fail to excute is_ipv6", K(ret));
    }
  }
  return ret;
}

int ObExprIsIpv6::calc_result1(ObObj& result, const ObObj& text, ObExprCtx& expr_ctx) const
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(expr_ctx.calc_buf_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("varchar buffer not init", K(ret));
  } else if (OB_FAIL(calc(result, text))) {
    LOG_WARN("fail to excute calc", K(ret));
  }
  return ret;
}

int ObExprIsIpv6::cg_expr(ObExprCGCtx& op_cg_ctx, const ObRawExpr& raw_expr, ObExpr& rt_expr) const
{
  UNUSED(op_cg_ctx);
  UNUSED(raw_expr);
  int ret = OB_SUCCESS;
  if (1 != rt_expr.arg_cnt_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("is_ipv6 expr should have one param", K(ret), K(rt_expr.arg_cnt_));
  } else if (OB_UNLIKELY(OB_ISNULL(rt_expr.args_) || OB_ISNULL(rt_expr.args_[0]))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("children of is_ipv6 expr is null", K(ret), K(rt_expr.args_));
  } else {
    rt_expr.eval_func_ = ObExprIsIpv6::calc_is_ipv6;
  }
  return ret;
}

int ObExprIsIpv6::calc_is_ipv6(const ObExpr& expr, ObEvalCtx& ctx, ObDatum& expr_datum)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(expr.eval_param_value(ctx))) {
    LOG_WARN("is_ipv6 expr eval param value failed", K(ret));
  } else {
    ObDatum& text = expr.locate_param_datum(ctx, 0);
    ObString m_text = text.get_string();
    if (text.is_null()) {
      expr_datum.set_int(0);
    } else if (OB_FAIL(is_ipv6(expr_datum, m_text))) {
      LOG_WARN("fail to excute is_ipv6", K(ret));
    }
  }
  return ret;
}

template <typename T>
int ObExprIsIpv6::is_ipv6(T& result, const ObString& text)
{
  char buf[MAX_IP_ADDR_LENGTH];
  int ipv6_ret = 1;
  int ret = OB_SUCCESS;
  if (MAX_IP_ADDR_LENGTH - 1 < text.length()) {
    ipv6_ret = 0;
  } else {
    MEMCPY(buf, text.ptr(), text.length());
    buf[text.length()] = '\0';
    in6_addr addr;
    bool is_ip_invaild;
    if (OB_FAIL(ObExprInetCommon::str_to_ipv6(text.length(), buf, is_ip_invaild, &addr))) {
      LOG_WARN("fail to excute str_to_ipv6", K(ret));
    } else {
      ipv6_ret = is_ip_invaild ? 0 : 1;
    }
  }
  result.set_int(ipv6_ret);
  return ret;
}

}  // namespace sql
}  // namespace oceanbase
