/**
 * 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/connect_by/ob_nested_loop_connect_by.h"
#include "sql/engine/connect_by/ob_connect_by_utility.h"
#include "sql/engine/expr/ob_expr_null_safe_equal.h"

using namespace oceanbase::sql;
using namespace oceanbase::common;

int ObConnectByPumpBase::deep_copy_row(const ObNewRow& src_row, const ObNewRow*& dst_row)
{
  int ret = OB_SUCCESS;
  char* buf = NULL;
  int64_t pos = sizeof(ObNewRow);
  ObNewRow* new_row = NULL;
  int64_t buf_len = src_row.get_deep_copy_size() + sizeof(ObNewRow);
  if (OB_ISNULL(buf = static_cast<char*>(allocator_.alloc(buf_len)))) {
    ret = OB_ALLOCATE_MEMORY_FAILED;
    LOG_WARN("alloc new row failed", K(ret), K(buf_len));
  } else if (OB_ISNULL(new_row = new (buf) ObNewRow())) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("new_row is null", K(ret), K(buf_len));
  } else if (OB_FAIL(new_row->deep_copy(src_row, buf, buf_len, pos))) {
    LOG_WARN("deep copy row failed", K(ret), K(buf_len), K(pos));
  } else {
    dst_row = new_row;
  }
  return ret;
}

int ObConnectByPumpBase::construct_pump_row(const ObNewRow* row)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(pump_row_desc_) || OB_ISNULL(row) || OB_UNLIKELY(row->is_invalid()) ||
      OB_UNLIKELY(shallow_row_.is_invalid())) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid data member", K(shallow_row_), KPC(pump_row_desc_), KPC(row), K(is_inited_), K(ret));
  } else if (pump_row_desc_->empty()) {
    // do nothing
    LOG_DEBUG("pump row empty", K(never_meet_cycle_), K(ret));
  } else if (shallow_row_.count_ != pump_row_desc_->count()) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("unexpected pump_row_desc", K(pump_row_desc_->count()), K(shallow_row_.count_), K(row->count_), K(ret));
  } else {
    int64_t cell_count = shallow_row_.count_;
    ObObj val;
    val.set_int(1);
    ObObj invalid_val(ObMaxType);
    for (int64_t i = 0; OB_SUCC(ret) && i < cell_count; ++i) {
      if (pump_row_desc_->at(i) == ObJoin::DUMMY_OUPUT) {
        shallow_row_.cells_[i] = val;
      } else if (pump_row_desc_->at(i) == ObJoin::UNUSED_POS) {
        shallow_row_.cells_[i] = invalid_val;
      } else if (OB_UNLIKELY(pump_row_desc_->at(i) + 1 > row->count_)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("unexpected pump_row_desc", K(i), K(pump_row_desc_->at(i)), K(row->count_));
      } else {
        shallow_row_.cells_[i] = row->cells_[pump_row_desc_->at(i)];
      }
    }
    LOG_DEBUG("construct pump row", K(*pump_row_desc_), K(shallow_row_), K(*row));
  }
  return ret;
}

int ObConnectByPumpBase::alloc_shallow_row_cells(const ObPhyOperator& left_op)
{
  int ret = OB_SUCCESS;
  void* cell_ptr = NULL;
  int64_t column_count = left_op.get_column_count();
  if (column_count <= 0) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(column_count));
  } else if (OB_ISNULL(cell_ptr = allocator_.alloc(column_count * sizeof(ObObj)))) {
    ret = OB_ALLOCATE_MEMORY_FAILED;
    LOG_ERROR("alloc memory for row failed", "size", column_count * sizeof(ObObj));
  } else {
    shallow_row_.cells_ = new (cell_ptr) common::ObObj[column_count];
    shallow_row_.count_ = column_count;
  }
  return ret;
}

int ObConnectByPumpBase::alloc_shallow_row_projector(const ObPhyOperator& left_op)
{
  int ret = OB_SUCCESS;
  int64_t projector_size = left_op.get_projector_size();
  const int32_t* projector = left_op.get_projector();
  int64_t size = projector_size * sizeof(int32_t);
  void* project_ptr = NULL;
  if (OB_ISNULL(project_ptr = allocator_.alloc(size))) {
    ret = OB_ALLOCATE_MEMORY_FAILED;
    LOG_ERROR("alloc memory for row failed", K(size));
  } else {
    shallow_row_.projector_ = static_cast<int32_t*>(project_ptr);
    shallow_row_.projector_size_ = projector_size;
  }

  for (int64_t i = 0; OB_SUCC(ret) && i < projector_size; ++i) {
    shallow_row_.projector_[i] = projector[i];
  }
  return ret;
}

int ObConnectByPumpBase::check_output_pseudo_columns()
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(pseudo_column_row_desc_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid data member", K(ret));
  } else if (pseudo_column_row_desc_->count() != CONNECT_BY_PSEUDO_COLUMN_CNT) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid row_desc", K(pseudo_column_row_desc_->count()), K(ret));
  } else {
    is_output_level_ = (ObJoin::UNUSED_POS != pseudo_column_row_desc_->at(LEVEL));
    is_output_cycle_ = (ObJoin::UNUSED_POS != pseudo_column_row_desc_->at(CONNECT_BY_ISCYCLE));
    is_output_leaf_ = (ObJoin::UNUSED_POS != pseudo_column_row_desc_->at(CONNECT_BY_ISLEAF));
  }
  return ret;
}

// loop only exists when there is prior in connect_by conditions.
int ObConnectByPumpBase::check_pump_row_desc()
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(pump_row_desc_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid data member", K(ret));
  } else {
    never_meet_cycle_ = pump_row_desc_->empty();
    LOG_DEBUG("check pump row desc", K(ret));
  }
  return ret;
}

uint64_t ObConnectByPump::ObHashColumn::inner_hash() const
{
  uint64_t result = 99194853094755497L;
  if (row_ != NULL && row_->is_valid()) {
    const ObObj* cells = row_->cells_;
    for (int64_t i = 0; i < row_->count_; ++i) {
      const ObObj& cell = cells[i];
      result = cell.hash_wy(result);
    }
  }
  return result;
}

bool ObConnectByPump::ObHashColumn::operator==(const ObHashColumn& other) const
{
  bool result = true;
  ObObj cmp_result;
  if (NULL == expr_ctx_) {
    result = false;
  } else {
    int tmp_ret = ObExprNullSafeEqual::compare_row2(cmp_result, row_, other.row_, *expr_ctx_);
    result = cmp_result.is_true() && OB_SUCCESS == tmp_ret;
  }
  return result;
}

void ObConnectByPump::close(ObIAllocator* allocator)
{
  reset();
  row_store_.reset();
  row_store_constructed_ = false;
  hash_table_.free(allocator);
}

// called in ObConnectBy rescan, reset to the state after open.
void ObConnectByPump::reset()
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(free_pump_node_stack(pump_stack_))) {
    LOG_ERROR("fail to free pump stack", K(ret));
  }
  pump_stack_.reset();
  hash_filter_rows_.reuse();
  for (int64_t i = 0; i < sys_path_buffer_.count(); i++) {
    ObString& str = sys_path_buffer_.at(i);
    if (NULL != str.ptr()) {
      allocator_.free(const_cast<char*>(str.ptr()));
    }
  }
  sys_path_buffer_.reset();
  cur_level_ = 1;
  if (NULL != connect_by_root_row_) {
    allocator_.free(const_cast<ObNewRow*>(connect_by_root_row_));
    connect_by_root_row_ = NULL;
  }
  // TODO:shanting not need to rebuild row_store_ if there is no push_down parameter
  if (true) {
    row_store_.reuse();
    row_store_constructed_ = false;
    hash_table_.reset();
  }
}

int ObConnectByPump::add_root_row(const ObNewRow* root_row, const ObNewRow& mock_right_row, const ObNewRow* output_row)
{
  UNUSED(mock_right_row);
  int ret = OB_SUCCESS;
  PumpNode node;
  node.level_ = 1;
  for (int64_t i = 0; OB_SUCC(ret) && i < connect_by_path_count_; i++) {
    if (OB_FAIL(node.sys_path_length_.push_back(0))) {
      LOG_WARN("array push back failed", K(ret));
    }
  }
  if (OB_FAIL(ret)) {
  } else if (OB_UNLIKELY(false == pump_stack_.empty())) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("pump stack must be empty", K(pump_stack_), K(ret));
  } else if (OB_ISNULL(root_row) || OB_ISNULL(output_row)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid parameter", K(root_row), K(output_row), K(ret));
  } else if (OB_FAIL(deep_copy_row(*root_row, node.pump_row_))) {
    LOG_WARN("deep copy row failed", K(ret));
  } else if (OB_FAIL(deep_copy_row(*output_row, node.output_row_))) {
    LOG_WARN("deep copy row failed", K(ret));
    //  } else if (is_output_cycle_ && OB_FAIL(deep_copy_row(mock_right_row, node.right_row_))) {
    //    LOG_WARN("deep copy row failed", K(ret));
  } else if (OB_FAIL(alloc_iter(node))) {
    LOG_WARN("alloc iterator failed", K(ret));
  } else if (OB_FAIL(push_back_node_to_stack(node))) {
    LOG_WARN("fail to push back row", K(ret));
  } else {
  }
  UNUSED(mock_right_row);
  return ret;
}

void ObConnectByPump::free_memory()
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(free_pump_node_stack(pump_stack_))) {
    LOG_ERROR("fail to free pump stack", K(ret));
  }

  if (shallow_row_.cells_ != NULL) {
    allocator_.free(shallow_row_.cells_);
    shallow_row_.cells_ = NULL;
  }

  if (shallow_row_.projector_ != NULL) {
    allocator_.free(shallow_row_.projector_);
    shallow_row_.projector_ = NULL;
  }

  if (prior_exprs_result_row_.cells_ != NULL) {
    allocator_.free(prior_exprs_result_row_.cells_);
    prior_exprs_result_row_.cells_ = NULL;
  }

  if (connect_by_root_row_ != NULL) {
    allocator_.free(const_cast<ObNewRow*>(connect_by_root_row_));
    connect_by_root_row_ = NULL;
  }
}

int ObConnectByPump::free_pump_node(PumpNode& pop_node)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(pop_node.pump_row_) || OB_ISNULL(pop_node.output_row_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid pop node", K(ret));
  } else {
    allocator_.free(const_cast<ObNewRow*>(pop_node.pump_row_));
    if (NULL != pop_node.right_row_) {
      allocator_.free(const_cast<ObNewRow*>(pop_node.right_row_));
    }
    allocator_.free(const_cast<ObNewRow*>(pop_node.output_row_));
    if (OB_ISNULL(pop_node.prior_exprs_result_)) {
    } else {
      allocator_.free(const_cast<ObNewRow*>(pop_node.prior_exprs_result_));
    }
    if (OB_ISNULL(pop_node.first_child_)) {
    } else {
      allocator_.free(const_cast<ObNewRow*>(pop_node.first_child_));
    }
    if (OB_NOT_NULL(pop_node.row_fetcher_.iterator_)) {
      allocator_.free(pop_node.row_fetcher_.iterator_);
    }
    pop_node.pump_row_ = NULL;
    pop_node.right_row_ = NULL;
    pop_node.output_row_ = NULL;
    pop_node.prior_exprs_result_ = NULL;
    pop_node.first_child_ = NULL;
    pop_node.row_fetcher_.iterator_ = NULL;
  }
  return ret;
}

int ObConnectByPump::free_pump_node_stack(ObIArray<PumpNode>& stack)
{
  int ret = OB_SUCCESS;
  PumpNode pop_node;
  while (OB_SUCC(ret) && false == stack.empty()) {
    if (OB_FAIL(stack.pop_back(pop_node))) {
      LOG_WARN("fail to pop back pump_stack", K(ret));
    } else if (OB_FAIL(free_pump_node(pop_node))) {
      LOG_WARN("free pump node failed", K(ret));
    }
  }
  return ret;
}

int ObConnectByPump::get_top_pump_node(PumpNode*& node)
{
  int ret = OB_SUCCESS;
  if (0 == pump_stack_.count()) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("pump stack is empty", K(ret));
  } else {
    node = &pump_stack_.at(pump_stack_.count() - 1);
  }
  return ret;
}

int ObConnectByPump::push_back_node_to_stack(PumpNode& node)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(calc_prior_and_check_cycle(node, true /* add node to hashset */))) {
    LOG_WARN("fail to calc path node", K(ret));
  } else if (false == node.is_cycle_ && OB_FAIL(pump_stack_.push_back(node))) {
    LOG_WARN("fail to push back row", K(ret));
  }

  if (OB_FAIL(ret)) {  // if fail free memory
    if (node.prior_exprs_result_ != NULL) {
      allocator_.free(const_cast<ObNewRow*>(node.prior_exprs_result_));
      node.prior_exprs_result_ = NULL;
    }
  }
  return ret;
}

int ObConnectByPump::alloc_prior_row_cells(uint64_t row_count)
{
  int ret = OB_SUCCESS;
  void* cell_ptr = NULL;
  if (row_count <= 0) {
  } else if (OB_ISNULL(cell_ptr = allocator_.alloc(row_count * sizeof(ObObj)))) {
    ret = OB_ALLOCATE_MEMORY_FAILED;
    LOG_ERROR("alloc memory for row failed", "size", row_count * sizeof(ObObj));
  } else {
    prior_exprs_result_row_.cells_ = new (cell_ptr) common::ObObj[row_count];
    prior_exprs_result_row_.count_ = row_count;
  }
  return ret;
}

int ObConnectByPump::alloc_iter(PumpNode& pop_node)
{
  int ret = OB_SUCCESS;
  void* buf = allocator_.alloc(sizeof(ObChunkRowStore::Iterator));
  if (OB_ISNULL(buf)) {
    ret = OB_ALLOCATE_MEMORY_FAILED;
    LOG_WARN("failed to allocate memory", K(ret));
  } else {
    pop_node.row_fetcher_.iterator_ = new (buf) ObChunkRowStore::Iterator();
  }
  return ret;
}

int ObConnectByPump::init(const ObConnectBy& connect_by, common::ObExprCtx* expr_ctx)
{
  int ret = OB_SUCCESS;
  const ObPhyOperator* left_op = NULL;
  if (OB_UNLIKELY(is_inited_)) {
    ret = OB_INIT_TWICE;
    LOG_WARN("init twice", K(ret));
  } else if (OB_UNLIKELY(2 != connect_by.get_child_num())) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid child num", K(ret));
  } else if (OB_ISNULL(left_op = connect_by.get_child(0))) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("left operator is NULL", K(ret));
  } else if (OB_FAIL(alloc_shallow_row_cells(*left_op))) {
    LOG_WARN("fail to alloc shallow row cells", K(ret));
  } else if (OB_FAIL(alloc_shallow_row_projector(*left_op))) {
    LOG_WARN("fail to alloc shallow row projector", K(ret));
  } else if (OB_FAIL(alloc_prior_row_cells(connect_by.get_connect_by_prior_exprs()->get_size()))) {
    LOG_WARN("fail to alloc prior exprs result row", K(ret));
  } else if (OB_FAIL(hash_filter_rows_.create(CONNECT_BY_TREE_HEIGHT))) {
    LOG_WARN("create hash set failed", K(ret));
  } else {
    pump_row_desc_ = connect_by.get_pump_row_desc();
    pseudo_column_row_desc_ = connect_by.get_pseudo_column_row_desc();
    connect_by_prior_exprs_ = connect_by.get_connect_by_prior_exprs();
    expr_ctx_ = expr_ctx;
    connect_by_ = &connect_by;
    connect_by_path_count_ = connect_by.get_sys_connect_by_path_expression_count();
    is_nocycle_ = connect_by.get_nocycle();
    DLIST_FOREACH(calc_node, connect_by.sys_connect_exprs_)
    {
      if (OB_FAIL(sys_path_buffer_.push_back(ObString()))) {
        LOG_WARN("init sys path buffer failed", K(ret));
      }
    }
    if (OB_FAIL(check_output_pseudo_columns())) {
      LOG_WARN("fail to check output pseudo columns", K(ret));
    } else if (OB_FAIL(check_pump_row_desc())) {
      LOG_WARN("fail to check pump row desc", K(ret));
    } else {
      is_inited_ = true;
    }
  }
  return ret;
}

int ObConnectByPump::join_right_table(PumpNode& node, bool& matched, ObPhyOperator::ObPhyOperatorCtx* phy_ctx)
{
  int ret = OB_SUCCESS;
  const ObNewRow* output_row = NULL;
  matched = false;
  ObConnectBy::ObConnectByCtx* join_ctx = static_cast<ObConnectBy::ObConnectByCtx*>(phy_ctx);
  if (OB_ISNULL(join_ctx)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("join ctx is null", K(ret));
  } else if (FALSE_IT(join_ctx->left_row_ = node.pump_row_)) {
  } else if (OB_NOT_NULL(node.first_child_)) {
    join_ctx->right_row_ = node.first_child_;
    node.first_child_ = NULL;
    matched = true;
  } else if (OB_FAIL(connect_by_->set_level_as_param(*join_ctx, node.level_ + 1))) {
    LOG_WARN("set current level as param failed", K(ret));
  } else {
    while (OB_SUCC(ret) && false == matched) {
      if (OB_FAIL(node.row_fetcher_.get_next_row(const_cast<ObNewRow*&>(join_ctx->right_row_)))) {
        if (OB_ITER_END != ret) {
          LOG_WARN("get row from row store failed", K(ret));
        }
      } else if (OB_FAIL(connect_by_->calc_other_conds(*join_ctx, matched))) {
        LOG_WARN("calc other conds failed", K(ret));
      } else if (matched) {
        PumpNode next_node;
        next_node.level_ = node.level_ + 1;
        if (next_node.level_ >= CONNECT_BY_MAX_NODE_NUM) {
          ret = OB_ERR_CBY_NO_MEMORY;
          uint64_t current_level = node.level_;
          int64_t max_node_num = CONNECT_BY_MAX_NODE_NUM;
          LOG_WARN("connect by reach memory limit", K(ret), K(current_level), K(max_node_num));
        }
        for (int64_t i = 0; OB_SUCC(ret) && i < connect_by_path_count_; i++) {
          if (OB_FAIL(next_node.sys_path_length_.push_back(0))) {
            LOG_WARN("array push back failed", K(ret));
          }
        }
        if (OB_FAIL(ret)) {
        } else if (OB_FAIL(connect_by_->join_rows(*join_ctx, output_row))) {
          LOG_WARN("join rows failed", K(ret));
        } else if (OB_FAIL(construct_pump_row(join_ctx->right_row_))) {
          LOG_WARN("construct pump node failed", K(ret));
        } else if (OB_FAIL(deep_copy_row(shallow_row_, next_node.pump_row_))) {
          LOG_WARN("deep copy row failed", K(ret));
        } else if (OB_FAIL(deep_copy_row(*output_row, next_node.output_row_))) {
          LOG_WARN("deep copy row failed", K(ret));
        } else if (OB_FAIL(deep_copy_row(*join_ctx->right_row_, next_node.right_row_))) {
          LOG_WARN("deep copy row failed", K(ret));
        } else if (OB_FAIL(alloc_iter(next_node))) {
          LOG_WARN("alloc iterator failed", K(ret));
        } else if (OB_FAIL(push_back_node_to_stack(next_node))) {
          LOG_WARN("push back node to stack failed", K(ret));
        } else if (next_node.is_cycle_) {
          // nocycle mode, if the matched child node is the same as an ancestor node, then abandon
          // this child node and continue searching
          matched = false;
          if (OB_FAIL(free_pump_node(next_node))) {
            LOG_WARN("free pump node failed", K(ret));
          }
        }
      }
    }
  }

  if (OB_ITER_END == ret) {
    ret = OB_SUCCESS;
  }
  return ret;
}

int ObConnectByPump::get_next_row(ObPhyOperator::ObPhyOperatorCtx* phy_ctx)
{
  int ret = OB_SUCCESS;
  bool matched = false;
  PumpNode pop_node;
  while (OB_SUCC(ret) && false == matched && pump_stack_.count() > 0) {
    PumpNode& node = pump_stack_.at(pump_stack_.count() - 1);
    cur_level_ = node.level_ + 1;
    if (OB_FAIL(join_right_table(node, matched, phy_ctx))) {
      LOG_WARN("join right table failed", K(ret));
    } else if (false == matched) {
      if (OB_FAIL(pump_stack_.pop_back(pop_node))) {
        LOG_WARN("pump stack pop back failed", K(ret));
      } else {
        // To pop a node in /pump_stack, you need to add the ObString of all sys_connect_by_path expressions in
        // sys_path_buffer_ All subtract the part corresponding to pop_node at the end.
        if (pump_stack_.empty()) {
          for (int64_t i = 0; i < sys_path_buffer_.count(); i++) {
            ObString& str = sys_path_buffer_.at(i);
            str.set_length(0);
          }
        } else {
          PumpNode& parent = pump_stack_.at(pump_stack_.count() - 1);
          if (parent.sys_path_length_.count() != sys_path_buffer_.count()) {
            ret = OB_ERR_UNEXPECTED;
            LOG_WARN("different sys_connect_by_path size", K(ret));
          } else {
            for (int64_t i = 0; i < sys_path_buffer_.count(); i++) {
              ObString& str = sys_path_buffer_.at(i);
              str.set_length(parent.sys_path_length_.at(i));
            }
          }
        }
      }
      if (OB_SUCC(ret) && NULL != pop_node.prior_exprs_result_) {
        ObHashColumn hash_col;
        hash_col.init(pop_node.prior_exprs_result_, expr_ctx_);
        if (OB_FAIL(hash_filter_rows_.erase_refactored(hash_col))) {
          LOG_WARN("fail to erase prior_exprs_result from hashset", K(ret), KPC(pop_node.prior_exprs_result_));
        }
      }
      if (OB_SUCC(ret) && OB_FAIL(free_pump_node(pop_node))) {
        LOG_WARN("free pump node failed", K(ret));
      }
    }
  }
  if (OB_SUCC(ret) && pump_stack_.empty()) {
    ret = OB_ITER_END;
    if (NULL != connect_by_root_row_) {
      allocator_.free(const_cast<ObNewRow*>(connect_by_root_row_));
      connect_by_root_row_ = NULL;
    }
  }

  return ret;
}

int ObConnectByPump::calc_prior_and_check_cycle(PumpNode& node, bool set_refactored)
{
  int ret = OB_SUCCESS;
  if (never_meet_cycle_) {
  } else {
    if (OB_ISNULL(connect_by_prior_exprs_) || OB_ISNULL(expr_ctx_)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("invalid data member", KPC(connect_by_prior_exprs_), K(ret));
    } else if (OB_UNLIKELY(connect_by_prior_exprs_->get_size() == 0)) {
      if (node.level_ > 1) {
        node.is_cycle_ = true;
        if (false == is_nocycle_) {
          ret = OB_ERR_CBY_LOOP;
        }
      }
    } else if (connect_by_prior_exprs_->get_size() > 0) {
      // allocate prior_expr_result_row
      int result_cnt = connect_by_prior_exprs_->get_size();
      if (OB_ISNULL(prior_exprs_result_row_.cells_) || OB_UNLIKELY(result_cnt != prior_exprs_result_row_.count_)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("not init", K(ret));
      } else {
        // calc prior expr result
        int idx = 0;
        DLIST_FOREACH(expr, *connect_by_prior_exprs_)
        {
          if (OB_FAIL(expr->calc(*expr_ctx_, *node.pump_row_, prior_exprs_result_row_.cells_[idx]))) {
            LOG_WARN("fail to calc prior expr", KPC(expr));
          } else {
            ++idx;
          }
        }
      }

      LOG_DEBUG("connect by after prior exprs", K(prior_exprs_result_row_), K(cur_level_));
      // check cycle and add node
      if (OB_SUCC(ret)) {
        const ObNewRow* check_row = NULL;
        if (OB_FAIL(set_refactored && deep_copy_row(prior_exprs_result_row_, node.prior_exprs_result_))) {
          LOG_WARN("deep copy row failed", K(ret));
        } else if (FALSE_IT(check_row = set_refactored ? node.prior_exprs_result_ : &prior_exprs_result_row_)) {
        } else if (OB_FAIL(check_cycle(*check_row, set_refactored))) {
          if (OB_ERR_CBY_LOOP == ret) {
            node.is_cycle_ = true;
            if (is_nocycle_) {
              ret = OB_SUCCESS;
            }
          } else {
            LOG_WARN("fail to check cycle", K(ret), K(node), K(prior_exprs_result_row_));
          }
        } else {
          node.is_cycle_ = false;
        }
      }
    }
  }
  return ret;
}

int ObConnectByPump::check_cycle(const ObNewRow& row, bool set_refactored)
{
  int ret = OB_SUCCESS;

  if (OB_ISNULL(expr_ctx_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid data member", K(ret));
  } else {
    ObHashColumn hash_col;
    hash_col.init(&row, expr_ctx_);
    if (OB_FAIL(hash_filter_rows_.exist_refactored(hash_col))) {
      if (OB_HASH_NOT_EXIST == ret) {
        if (set_refactored && OB_FAIL(hash_filter_rows_.set_refactored(hash_col))) {
          LOG_WARN("Failed to insert into hashset", K(ret), K(row));
        } else {
          ret = OB_SUCCESS;
        }
      } else if (OB_HASH_EXIST == ret) {
        ret = OB_ERR_CBY_LOOP;
        LOG_WARN("CONNECT BY loop in user data", K(ret), K(row));

      } else {
        LOG_WARN("Failed to find in hashset", K(ret));
      }
    }
  }

  return ret;
}

int ObConnectByPump::check_child_cycle(const ObNewRow& right_row, PumpNode& node)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(construct_pump_row(&right_row))) {
    LOG_WARN("construct pump node failed", K(ret));
  } else if (FALSE_IT(node.pump_row_ = &shallow_row_)) {
  } else if (OB_FAIL(calc_prior_and_check_cycle(node, false))) {
    LOG_WARN("calc prior expr and check cycle failed", K(ret));
  }
  return ret;
}

int ObConnectByPump::get_sys_path(uint64_t sys_connect_by_path_id, ObString& parent_path)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(sys_path_buffer_.count() <= sys_connect_by_path_id)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid sys connect by path id", K(ret), K(sys_connect_by_path_id));
  } else {
    ObString& str = sys_path_buffer_.at(sys_connect_by_path_id);
    parent_path.assign_ptr(str.ptr(), str.length());
  }
  return ret;
}

int ObConnectByPump::concat_sys_path(uint64_t sys_connect_by_path_id, const ObString& cur_path)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(pump_stack_.empty()) || OB_UNLIKELY(sys_path_buffer_.count() <= sys_connect_by_path_id)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("invalid sys connect by path id", K(ret), K(sys_connect_by_path_id));
  } else {
    ObString& str = sys_path_buffer_.at(sys_connect_by_path_id);
    if (0 == str.size()) {
      char* buf = NULL;
      int64_t buf_size = SYS_PATH_BUFFER_INIT_SIZE;
      while (buf_size < cur_path.length()) {
        buf_size *= 2;
      }
      if (OB_ISNULL(buf = static_cast<char*>(allocator_.alloc(buf_size)))) {
        LOG_WARN("allocate buf failed", K(ret), K(buf_size));
      } else {
        str.assign_buffer(buf, buf_size);
      }
    } else if (str.length() + cur_path.length() > str.size()) {
      int64_t buf_size = str.size();
      while (buf_size < str.length() + cur_path.length()) {
        buf_size *= 2;
      }
      char* buf = NULL;
      ObString new_str;
      if (OB_ISNULL(buf = static_cast<char*>(allocator_.alloc(buf_size)))) {
        LOG_WARN("allocate buf failed", K(ret), K(buf_size));
      } else if (FALSE_IT(new_str.assign_buffer(buf, buf_size))) {
      } else if (str.length() != new_str.write(str.ptr(), str.length())) {
        LOG_WARN("copy origin string failed", K(ret));
      } else {
        allocator_.free(const_cast<char*>(str.ptr()));
        str.assign_buffer(new_str.ptr(), new_str.size());
      }
    }
    if (OB_SUCC(ret) && cur_path.length() != str.write(cur_path.ptr(), cur_path.length())) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("write cur_path to sys_path_buffer failed", K(ret));
    }
    if (OB_SUCC(ret)) {
      PumpNode& node = pump_stack_.at(pump_stack_.count() - 1);
      if (sys_connect_by_path_id >= node.sys_path_length_.count()) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("invalid pump node", K(ret), K(sys_connect_by_path_id), K(node.sys_path_length_.count()));
      } else {
        uint64_t& length = node.sys_path_length_.at(sys_connect_by_path_id);
        length = str.length();
      }
    }
  }
  return ret;
}

int ObConnectByPump::ConnectByHashTable::init(ObIAllocator& alloc)
{
  int ret = OB_SUCCESS;
  if (!inited_) {
    void* alloc_buf = alloc.alloc(sizeof(ModulePageAllocator));
    void* bucket_buf = alloc.alloc(sizeof(BucketArray));
    void* cell_buf = alloc.alloc(sizeof(AllCellArray));
    if (OB_ISNULL(bucket_buf) || OB_ISNULL(cell_buf) || OB_ISNULL(alloc_buf)) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      if (OB_NOT_NULL(bucket_buf)) {
        alloc.free(bucket_buf);
      }
      if (OB_NOT_NULL(cell_buf)) {
        alloc.free(cell_buf);
      }
      if (OB_NOT_NULL(alloc_buf)) {
        alloc.free(alloc_buf);
      }
      LOG_WARN("failed to alloc memory", K(ret));
    } else {
      ht_alloc_ = new (alloc_buf) ModulePageAllocator(alloc);
      ht_alloc_->set_label("HtAlloc");
      buckets_ = new (bucket_buf) BucketArray(*ht_alloc_);
      all_cells_ = new (cell_buf) AllCellArray(*ht_alloc_);
      inited_ = true;
    }
  }
  return ret;
}

void ObConnectByPump::ConnectByHashTable::reverse_bucket_cells()
{
  HashTableCell* head_tuple = NULL;
  HashTableCell* cur_tuple = NULL;
  HashTableCell* next_tuple = NULL;
  for (int64_t bucket_idx = 0; bucket_idx < nbuckets_; bucket_idx++) {
    if (NULL != (head_tuple = buckets_->at(bucket_idx))) {
      cur_tuple = head_tuple->next_tuple_;
      head_tuple->next_tuple_ = NULL;
      while (NULL != cur_tuple) {
        next_tuple = cur_tuple->next_tuple_;
        cur_tuple->next_tuple_ = head_tuple;
        head_tuple = cur_tuple;
        cur_tuple = next_tuple;
      }
      buckets_->at(bucket_idx) = head_tuple;
    }
  }
}

int ObConnectByPump::calc_hash_value(
    const ObArray<ObSqlExpression*>& hash_exprs, const ObNewRow& row, ObExprCtx& expr_ctx, uint64_t& hash_value) const
{
  int ret = OB_SUCCESS;
  ObObj obj_value;
  hash_value = 0;
  if (OB_ISNULL(connect_by_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("connect by is null", K(ret));
  }
  for (int64_t i = 0; OB_SUCC(ret) && i < hash_exprs.count(); i++) {
    ObSqlExpression* hash_expr = hash_exprs.at(i);
    ObConnectBy::EqualConditionInfo equal_cond_info = connect_by_->equal_cond_infos_.at(i);
    if (OB_ISNULL(hash_expr)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("hash probe expr is null", K(ret));
    } else if (OB_FAIL(hash_expr->calc(expr_ctx, row, obj_value))) {
      LOG_WARN("calc left expr value failed", K(ret));
    } else if (obj_value.get_type() != equal_cond_info.cmp_type_ ||
               (ObStringTC == obj_value.get_type_class() && obj_value.get_collation_type() != equal_cond_info.ctype_)) {
      ObObj buf_obj;
      const ObObj* res_obj = NULL;
      EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE);
      cast_ctx.dest_collation_ = equal_cond_info.ctype_;
      if (OB_FAIL(ObObjCaster::to_type(equal_cond_info.cmp_type_, cast_ctx, obj_value, buf_obj, res_obj))) {
        LOG_WARN("failed to cast obj", K(ret), K(equal_cond_info.cmp_type_), K(obj_value));
      } else if (OB_ISNULL(res_obj)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("res_obj is null", K(ret));
      } else {
        hash_value = res_obj->hash_murmur(hash_value);
      }
    } else {
      hash_value = obj_value.hash_murmur(hash_value);
    }
  }
  return ret;
}

int ObConnectByPump::build_hash_table(ObIAllocator& alloc)
{
  int ret = OB_SUCCESS;
  int64_t row_store_cnt = row_store_.get_row_cnt();
  int64_t bucket_num = 32;
  // The order of the rows in ChunkRowStore is the same as the order of reading in the right table,
  // so traverse the ChunkRowStore and add each row to the head of the linked list of the corresponding bucket
  // The order of the rows in the linked list of each bucket is opposite to the order read in the right table.
  // Although it does not affect the correctness, in order to be the same as the result before the modification, try to
  // be compatible with oracle, Processed here.
  // 1. If you can apply for the space to record the pointer of the tuple at the end of each bucket,
  //    then use this array to ensure that the order is ideal when creating the hash table
  // 2. If you can't apply for space, then after the hash table is built, reverse each bucket list. At this time,
  // need_reverse_bucket is true.
  bool need_reverse_bucket = true;
  while (bucket_num < row_store_cnt * 2) {
    bucket_num *= 2;
  }
  if (OB_FAIL(hash_table_.init(alloc))) {
    LOG_WARN("init hash table failed", K(ret));
  } else {
    hash_table_.nbuckets_ = bucket_num;
    hash_table_.row_count_ = row_store_.get_row_cnt();
    hash_table_.buckets_->reuse();
    hash_table_.all_cells_->reuse();
  }

  HashTableCell** bucket_end_cell = NULL;
  if (OB_FAIL(ret)) {
  } else if (OB_UNLIKELY(0 == connect_by_->hash_key_exprs_.count()) || OB_ISNULL(expr_ctx_)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("hash key expr or expr_ctx_ is null", K(ret), K(connect_by_->hash_key_exprs_));
  } else if (OB_FAIL(hash_table_.buckets_->init(hash_table_.nbuckets_))) {
    LOG_WARN("alloc bucket array failed", K(ret), K(hash_table_.nbuckets_));
  } else if (0 < hash_table_.row_count_ && OB_FAIL(hash_table_.all_cells_->init(hash_table_.row_count_))) {
    LOG_WARN("alloc hash cell failed", K(ret), K(hash_table_.row_count_));
  } else if (OB_ISNULL(
                 bucket_end_cell = static_cast<HashTableCell**>(alloc.alloc(sizeof(HashTableCell*) * bucket_num)))) {
    need_reverse_bucket = true;
    LOG_TRACE("alloc bucket end cell failed, need reverse bucket", K(bucket_num));
  } else {
    for (int64_t i = 0; i < bucket_num; i++) {
      bucket_end_cell[i] = NULL;
    }
    need_reverse_bucket = false;
  }
  if (OB_FAIL(ret)) {
  } else {
    ObChunkRowStore::Iterator iterator;
    const ObChunkRowStore::StoredRow* store_row = NULL;
    ObNewRow* new_row = NULL;
    HashTableCell* tuple = NULL;
    int64_t cell_index = 0;
    int64_t bucket_id = 0;
    uint64_t hash_value = 0;
    if (OB_FAIL(row_store_.begin(iterator))) {
      LOG_WARN("begin row store failed", K(ret));
    }
    while (OB_SUCC(ret)) {
      if (OB_FAIL(iterator.get_next_row(store_row))) {
        if (OB_ITER_END != ret) {
          LOG_WARN("get next row failed", K(ret));
        }
      } else if (OB_ISNULL(store_row)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("row is null", K(ret));
      } else if (OB_FAIL(iterator.convert_to_row(store_row, new_row))) {
        LOG_WARN("convert to row failed", K(ret));
      } else if (OB_FAIL(calc_hash_value(connect_by_->hash_key_exprs_, *new_row, *expr_ctx_, hash_value))) {
        LOG_WARN("calc hash value", K(ret));
      } else {
        bucket_id = hash_value & (hash_table_.nbuckets_ - 1);
        tuple = &(hash_table_.all_cells_->at(cell_index));
        tuple->stored_row_ = store_row;
        if (need_reverse_bucket) {
          tuple->next_tuple_ = hash_table_.buckets_->at(bucket_id);
          hash_table_.buckets_->at(bucket_id) = tuple;
        } else {
          tuple->next_tuple_ = NULL;
          if (NULL == bucket_end_cell[bucket_id]) {
            hash_table_.buckets_->at(bucket_id) = tuple;
          } else {
            bucket_end_cell[bucket_id]->next_tuple_ = tuple;
          }
          bucket_end_cell[bucket_id] = tuple;
        }
        ++cell_index;
      }
    }
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
      if (need_reverse_bucket) {
        hash_table_.reverse_bucket_cells();
      }
      use_hash_ = true;
      LOG_TRACE("build hash table succeed", K(connect_by_->hash_key_exprs_), K(row_store_cnt), K(bucket_num));
    }
    if (!OB_ISNULL(bucket_end_cell)) {
      alloc.free(bucket_end_cell);
    }
  }
  return ret;
}

int ObConnectByPump::RowFetcher::init(
    ObConnectByPump& connect_by_pump, const ObArray<ObSqlExpression*>& hash_probe_exprs, const ObNewRow* left_row)
{
  int ret = OB_SUCCESS;
  use_hash_ = false;
  if (OB_ISNULL(iterator_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("row fetcher iterator is null", K(ret));
  } else if (OB_FAIL(connect_by_pump.row_store_.begin(*iterator_))) {
    LOG_WARN("init chunk row store iterator failed", K(ret));
  } else if (!connect_by_pump.use_hash_) {
  } else {
    uint64_t hash_value = 0;
    if (!connect_by_pump.hash_table_.inited_) {
      ret = OB_NOT_INIT;
      LOG_WARN("hash table not init", K(ret));
    } else if (OB_ISNULL(left_row) || OB_UNLIKELY(0 == hash_probe_exprs.count()) ||
               OB_ISNULL(connect_by_pump.expr_ctx_)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("left row or hash probe expr is null", K(left_row), K(hash_probe_exprs), K(connect_by_pump.expr_ctx_));
    } else if (OB_FAIL(connect_by_pump.calc_hash_value(
                   hash_probe_exprs, *left_row, *connect_by_pump.expr_ctx_, hash_value))) {
      LOG_WARN("calc hash value", K(ret));
    } else {
      use_hash_ = true;
      int64_t bucket_id = hash_value & (connect_by_pump.hash_table_.nbuckets_ - 1);
      tuple_ = connect_by_pump.hash_table_.buckets_->at(bucket_id);
    }
  }
  return ret;
}

int ObConnectByPump::RowFetcher::get_next_row(ObNewRow*& row)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(iterator_)) {
    ret = OB_NOT_INIT;
    LOG_WARN("row fetcher iterator is null", K(ret));
  } else if (!use_hash_) {
    if (OB_FAIL(iterator_->get_next_row(row))) {
      if (OB_ITER_END != ret) {
        LOG_WARN("get next row failed", K(ret));
      }
    }
  } else {
    if (NULL == tuple_) {
      ret = OB_ITER_END;
    } else if (OB_ISNULL(tuple_->stored_row_)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("store row is null", K(ret));
    } else if (OB_FAIL(iterator_->convert_to_row(tuple_->stored_row_, row))) {
      LOG_WARN("convert to row failed", K(ret));
    } else {
      tuple_ = tuple_->next_tuple_;
    }
  }
  return ret;
}
