/**
 * 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.
 */

#ifndef OCEANBASE_BASIC_OB_CHUNK_ROW_STORE_CPP_
#define OCEANBASE_BASIC_OB_CHUNK_ROW_STORE_CPP_

#define USING_LOG_PREFIX SQL_ENG

#include "sql/engine/basic/ob_chunk_row_store.h"
#include "lib/container/ob_se_array_iterator.h"
#include "lib/utility/ob_tracepoint.h"
#include "share/config/ob_server_config.h"

namespace oceanbase {
using namespace common;

namespace sql {
int ObChunkRowStore::BlockBuffer::init(char* buf, const int64_t buf_size)
{
  int ret = OB_SUCCESS;
  if (NULL == buf || buf_size <= 0) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret));
  } else {
    data_ = buf;
    cur_pos_ = 0;
    cap_ = buf_size - sizeof(BlockBuffer);
  }
  return ret;
}
namespace chunk_row_store {

template <typename T, typename B>
void pointer2off(T*& pointer, B* base)
{
  pointer = reinterpret_cast<T*>(reinterpret_cast<const char*>(pointer) - reinterpret_cast<const char*>(base));
}

template <typename T, typename B>
void off2pointer(T*& pointer, B* base)
{
  pointer = reinterpret_cast<T*>(reinterpret_cast<intptr_t>(pointer) + reinterpret_cast<char*>(base));
}

template <typename T, typename B>
void point2pointer(T*& dst_pointer, B* dst_base, T* src_pointer, const B* src_base)
{
  dst_pointer = reinterpret_cast<T*>(reinterpret_cast<char*>(dst_base) + reinterpret_cast<intptr_t>(src_pointer) -
                                     reinterpret_cast<const char*>(src_base));
}
}  // namespace chunk_row_store

inline int ObChunkRowStore::StoredRow::copy_row(
    const ObNewRow& r, char* buf, const int64_t size, const int64_t row_size, const uint32_t row_extend_size)
{
  int ret = OB_SUCCESS;
  if (payload_ != buf || size < 0) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret), KP(payload_), KP(buf), K(size));
  } else {
    cnt_ = static_cast<uint32_t>(r.get_count());
    // LOG_DEBUG("Molly copy row", K(r.get_count()), K(r.count_), K(r.projector_size_), K(cnt_));
    int64_t pos = sizeof(ObObj) * cnt_ + row_extend_size;
    row_size_ = static_cast<int32_t>(row_size);
    for (int64_t i = 0; OB_SUCC(ret) && i < cnt_; ++i) {
      ObObj* obj = new (&cells()[i]) ObObj();
      if (OB_FAIL(obj->deep_copy(r.get_cell(i), buf, size, pos))) {
        LOG_WARN("copy cell failed", K(ret));
      }
    }
  }

  return ret;
}

int ObChunkRowStore::StoredRow::assign(const StoredRow* sr)
{
  int ret = OB_SUCCESS;
  MEMCPY(this, static_cast<const void*>(sr), sr->row_size_);
  ObObj* src_cells = const_cast<ObObj*>(sr->cells());
  for (int64_t i = 0; i < cnt_; ++i) {
    if (cells()[i].need_deep_copy()) {
      chunk_row_store::point2pointer(cells()[i].v_.string_, this, src_cells[i].v_.string_, sr);
    }
  }

  return ret;
}

void ObChunkRowStore::StoredRow::unswizzling(char* base /*= NULL*/)
{
  if (NULL == base) {
    base = (char*)this;
  }
  for (int64_t i = 0; i < cnt_; ++i) {
    if (cells()[i].need_deep_copy()) {
      chunk_row_store::pointer2off(cells()[i].v_.string_, base);
    }
  }
  LOG_DEBUG("trace unswizzling", K(this));
}

void ObChunkRowStore::StoredRow::swizzling()
{
  for (int64_t i = 0; i < cnt_; ++i) {
    if (cells()[i].need_deep_copy()) {
      chunk_row_store::off2pointer(cells()[i].v_.string_, this);
    }
  }
}

int ObChunkRowStore::Block::add_row(
    const ObNewRow& row, const int64_t row_size, uint32_t row_extend_size, StoredRow** stored_row)
{
  int ret = OB_SUCCESS;
  BlockBuffer* buf = get_buffer();
  if (!buf->is_inited()) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret), K(buf), K(row_size));
  } else if (row_size > buf->remain()) {
    ret = OB_BUF_NOT_ENOUGH;
    LOG_WARN("buffer not enough", K(row_size), "remain", buf->remain());
  } else {
    StoredRow* sr = new (buf->head()) StoredRow;
    if (OB_FAIL(sr->copy_row(row, buf->head() + ROW_HEAD_SIZE, row_size - ROW_HEAD_SIZE, row_size, row_extend_size))) {
      LOG_WARN("copy row failed", K(ret), K(row), K(row_size));
    } else {
      if (OB_FAIL(buf->advance(row_size))) {
        LOG_WARN("fill buffer head failed", K(ret), K(buf), K(row_size));
      } else {
        rows_++;
        if (NULL != stored_row) {
          *stored_row = sr;
        }
      }
    }
    // LOG_DEBUG("RowStore blk add_row", K_(sr->cnt), K_(rows), K(row_size), K(buf->data_size()));
  }

  return ret;
}

int ObChunkRowStore::Block::copy_row(const StoredRow* stored_row)
{
  int ret = OB_SUCCESS;
  BlockBuffer* buf = get_buffer();
  int64_t row_size = stored_row->row_size_;
  if (!buf->is_inited()) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret), K(buf), K(row_size));
  } else {
    StoredRow* sr = new (buf->head()) StoredRow;
    sr->assign(stored_row);
    if (OB_FAIL(buf->advance(row_size))) {
      LOG_WARN("fill buffer head failed", K(ret), K(buf), K(row_size));
    } else {
      rows_++;
    }
  }

  return ret;
}

int ObChunkRowStore::Block::get_store_row(int64_t& cur_pos, const StoredRow*& sr)
{
  int ret = OB_SUCCESS;
  if (cur_pos >= blk_size_) {
    ret = OB_INDEX_OUT_OF_RANGE;
    LOG_WARN("invalid index", K(ret), K(cur_pos), K_(rows));
  } else {
    StoredRow* row = reinterpret_cast<StoredRow*>(&payload_[cur_pos]);
    cur_pos += row->row_size_;
    sr = row;
  }
  return ret;
}

int ObChunkRowStore::Block::unswizzling()
{
  int ret = OB_SUCCESS;
  int64_t cur_pos = 0;
  uint32_t i = 0;
  while (OB_SUCC(ret) && cur_pos < blk_size_ && i < rows_) {
    StoredRow* sr = reinterpret_cast<StoredRow*>(payload_ + cur_pos);
    sr->unswizzling();
    cur_pos += sr->row_size_;
    i++;
  }
  return ret;
}

int ObChunkRowStore::Block::swizzling(int64_t* col_cnt)
{
  int ret = OB_SUCCESS;
  int64_t cur_pos = 0;
  uint32_t i = 0;
  StoredRow* sr = NULL;
  while (OB_SUCC(ret) && cur_pos < blk_size_ && i < rows_) {
    sr = reinterpret_cast<StoredRow*>(&payload_[cur_pos]);
    sr->swizzling();
    cur_pos += sr->row_size_;
    i++;
  }

  if (OB_SUCC(ret) && NULL != sr && NULL != col_cnt) {
    *col_cnt = sr->cnt_;
  }
  return ret;
}

ObChunkRowStore::ObChunkRowStore(common::ObIAllocator* alloc /* = NULL */)
    : inited_(false),
      tenant_id_(0),
      label_(nullptr),
      ctx_id_(0),
      mem_limit_(0),
      cur_blk_(NULL),
      max_blk_size_(0),
      min_blk_size_(INT64_MAX),
      default_block_size_(BLOCK_SIZE),
      n_blocks_(0),
      col_count_(0),
      row_cnt_(0),
      enable_dump_(true),
      has_dumped_(false),
      dumped_row_cnt_(0),
      file_size_(0),
      n_block_in_file_(0),
      mem_hold_(0),
      mem_used_(0),
      allocator_(NULL == alloc ? &inner_allocator_ : alloc),
      row_store_mode_(STORE_MODE::WITHOUT_PROJECTOR),
      projector_(NULL),
      projector_size_(0),
      row_extend_size_(0),
      alloc_size_(0),
      free_size_(0),
      callback_(nullptr)
{
  io_.fd_ = -1;
  io_.dir_id_ = -1;
}

int ObChunkRowStore::init(int64_t mem_limit, uint64_t tenant_id /* = common::OB_SERVER_TENANT_ID */,
    int64_t mem_ctx_id /* = common::ObCtxIds::DEFAULT_CTX_ID */,
    const char* label /* = common::ObNewModIds::OB_SQL_CHUNK_ROW_STORE) */, bool enable_dump /* = true */,
    STORE_MODE row_store_mode /* WITHOUT_PROJECTOR */, uint32_t row_extend_size /* = 0 */
)
{
  int ret = OB_SUCCESS;
  if (inited_) {
    ret = OB_INIT_TWICE;
    LOG_WARN("init twice", K(ret));
  } else {
    enable_dump_ = enable_dump;
    tenant_id_ = tenant_id;
    ctx_id_ = mem_ctx_id;
    label_ = label;
    if (0 == GCONF._chunk_row_store_mem_limit) {
      mem_limit_ = mem_limit;
    } else {
      mem_limit_ = GCONF._chunk_row_store_mem_limit;
    }
    inited_ = true;
    default_block_size_ = BLOCK_SIZE;
    max_blk_size_ = default_block_size_;
    min_blk_size_ = INT64_MAX;
    io_.fd_ = -1;
    row_store_mode_ = row_store_mode;
    row_extend_size_ = row_extend_size;
  }
  return ret;
}

void ObChunkRowStore::reset()
{
  int ret = OB_SUCCESS;
  tenant_id_ = common::OB_SERVER_TENANT_ID;
  label_ = common::ObModIds::OB_SQL_ROW_STORE;
  ctx_id_ = common::ObCtxIds::DEFAULT_CTX_ID;

  mem_limit_ = 0;
  row_cnt_ = 0;

  if (is_file_open()) {
    if (OB_FAIL(FILE_MANAGER_INSTANCE_V2.remove(io_.fd_))) {
      LOG_WARN("remove file failed", K(ret), K_(io_.fd));
    } else {
      LOG_TRACE("close file success", K(ret), K_(io_.fd));
    }
    io_.fd_ = -1;
  }
  file_size_ = 0;
  n_block_in_file_ = 0;

  while (!blocks_.is_empty()) {
    Block* item = blocks_.remove_first();
    mem_hold_ -= item->get_buffer()->mem_size();
    mem_used_ -= item->get_buffer()->mem_size();
    callback_free(item->get_buffer()->mem_size());
    if (NULL != item) {
      allocator_->free(item);
    }
  }
  blocks_.reset();
  cur_blk_ = NULL;
  while (!free_list_.is_empty()) {
    Block* item = free_list_.remove_first();
    mem_hold_ -= item->get_buffer()->mem_size();
    callback_free(item->get_buffer()->mem_size());
    if (NULL != item) {
      allocator_->free(item);
    }
  }
  free_list_.reset();

  LOG_DEBUG("mem usage after free", K(mem_hold_), K(mem_used_));
  max_blk_size_ = 0;
  n_blocks_ = 0;
  row_cnt_ = 0;

  if (NULL != projector_) {
    allocator_->free(projector_);
  }
  projector_ = NULL;
  projector_size_ = 0;
  if (alloc_size_ != free_size_) {
    LOG_ERROR("It may has memory no free", K(alloc_size_), K(free_size_), K(lbt()));
  }
  alloc_size_ = 0;
  free_size_ = 0;
  callback_ = nullptr;

  inited_ = false;
}

void ObChunkRowStore::reuse()
{
  int ret = OB_SUCCESS;
  if (is_file_open()) {
    if (OB_FAIL(FILE_MANAGER_INSTANCE_V2.remove(io_.fd_))) {
      LOG_WARN("remove file failed", K(ret), K_(io_.fd));
    } else {
      LOG_TRACE("close file success", K(ret), K_(io_.fd));
    }
    io_.fd_ = -1;
  }
  file_size_ = 0;
  n_block_in_file_ = 0;

  while (!blocks_.is_empty()) {
    Block* item = blocks_.remove_first();
    mem_hold_ -= item->get_buffer()->mem_size();
    mem_used_ -= item->get_buffer()->mem_size();
    callback_free(item->get_buffer()->mem_size());
    if (NULL != item) {
      allocator_->free(item);
    }
  }
  blocks_.reset();
  cur_blk_ = NULL;
  while (!free_list_.is_empty()) {
    Block* item = free_list_.remove_first();
    mem_hold_ -= item->get_buffer()->mem_size();
    callback_free(item->get_buffer()->mem_size());
    if (NULL != item) {
      allocator_->free(item);
    }
  }

  LOG_DEBUG("mem usage after free", K(mem_hold_), K(mem_used_), K(blocks_.get_size()));
  mem_hold_ = 0;
  mem_used_ = mem_hold_;
  max_blk_size_ = default_block_size_;
  n_blocks_ = 0;
  row_cnt_ = 0;

  if (NULL != projector_) {
    allocator_->free(projector_);
  }
  projector_ = NULL;
  projector_size_ = 0;
}

void* ObChunkRowStore::alloc_blk_mem(const int64_t size, const bool for_iterator)
{
  void* blk = NULL;
  int ret = OB_SUCCESS;
  if (size < 0) {
    LOG_WARN("invalid argument", K(size));
  } else {
    ObMemAttr attr(tenant_id_, label_, ctx_id_);
    void* mem = allocator_->alloc(size, attr);
    if (NULL == mem) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_WARN("alloc memory failed", K(ret), K(size), KP(mem), K(label_), K(ctx_id_), K(mem_limit_), K(enable_dump_));
    } else {
      blk = static_cast<char*>(mem);
      if (!for_iterator) {
        mem_hold_ += size;
      }
      callback_alloc(size);
    }
  }
  return blk;
}

void ObChunkRowStore::free_blk_mem(void* mem, const int64_t size /* = 0 */)
{
  if (NULL != mem) {
    LOG_DEBUG("free blk memory", K(size), KP(mem));
    allocator_->free(mem);
    mem_hold_ -= size;
    callback_free(size);
  }
}

void ObChunkRowStore::free_block(Block* item)
{
  if (NULL != item) {
    BlockBuffer* buffer = item->get_buffer();
    if (!buffer->is_empty()) {
      mem_used_ -= buffer->mem_size();
    }
    mem_hold_ -= buffer->mem_size();
    callback_free(buffer->mem_size());
    allocator_->free(item);
  }
}

void ObChunkRowStore::free_blk_list()
{
  Block* item = blocks_.remove_first();
  while (item != NULL) {
    free_block(item);
    item = blocks_.remove_first();
  }
  blocks_.reset();
}

// free mem of specified size
bool ObChunkRowStore::shrink_block(int64_t size)
{
  int64_t freed_size = 0;
  bool succ = false;
  int ret = OB_SUCCESS;
  if ((0 == blocks_.get_size() && 0 == free_list_.get_size()) || 0 == size) {
    LOG_DEBUG("RowStore no need to shrink", K(size), K(blocks_.get_size()));
  } else {
    Block* item = free_list_.remove_first();
    // free those blocks haven't been used yet
    while (NULL != item) {
      freed_size += item->get_buffer()->mem_size();
      LOG_DEBUG("RowStore shrink free empty", K(size), K(freed_size), K_(item->blk_size));
      free_block(item);
      item = free_list_.remove_first();
    }

    item = blocks_.remove_first();
    while (OB_SUCC(ret) && NULL != item) {
      if (OB_FAIL(dump_one_block(item->get_buffer()))) {
        LOG_WARN("dump failed when shrink blk",
            K(ret),
            K(item),
            K(item->get_buffer()->data_size()),
            K(item->get_buffer()->mem_size()),
            K(size));
      }
      freed_size += item->get_buffer()->mem_size();
      LOG_DEBUG("RowStore shrink dump and free", K(size), K(freed_size), K(item->get_buffer()->mem_size()), K(item));
      free_block(item);
      item = blocks_.remove_first();
    }
  }
  LOG_DEBUG("RowStore shrink_block", K(ret), K(freed_size), K(size));
  if (freed_size >= size) {
    succ = true;
  }
  return succ;
}

int ObChunkRowStore::init_block_buffer(void* mem, const int64_t size, Block*& block)
{
  int ret = OB_SUCCESS;
  block = new (mem) Block;
  BlockBuffer* blkbuf = new (static_cast<char*>(mem) + size - sizeof(BlockBuffer)) BlockBuffer;
  if (OB_FAIL(blkbuf->init(static_cast<char*>(mem), size))) {
    LOG_WARN("init shrink buffer failed", K(ret));
  } else {
    if (OB_FAIL(blkbuf->advance(sizeof(Block)))) {
      LOG_WARN("fill buffer head failed", K(ret), K(static_cast<void*>(blkbuf->data())), K(sizeof(Block)));
    } else {
      block->set_block_size(static_cast<uint32_t>(blkbuf->capacity()));
      block->next_ = NULL;
      block->rows_ = 0;
    }
  }
  return ret;
}

int ObChunkRowStore::alloc_block_buffer(
    Block*& block, const int64_t data_size, const int64_t min_size, const bool for_iterator)
{
  int ret = OB_SUCCESS;
  int64_t size = std::max(min_size, data_size);
  size = next_pow2(size);
  if (!is_inited()) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret));
  } else {
    void* mem = alloc_blk_mem(size, for_iterator);
    if (OB_ISNULL(mem)) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_WARN("alloc memory failed", K(ret), K(size));
    } else if (OB_FAIL(init_block_buffer(mem, size, block))) {
      free_blk_mem(mem, size);
    } else if (!for_iterator) {
      if (size > max_blk_size_) {
        max_blk_size_ = size;
      } else if (size < min_blk_size_) {
        min_blk_size_ = size;
      }
    }
    LOG_DEBUG("RowStore alloc block", K(ret), K_(max_blk_size), K(data_size), K(min_size), K(for_iterator));
  }
  return ret;
}

int ObChunkRowStore::alloc_block_buffer(Block*& block, const int64_t data_size, const bool for_iterator)
{
  int64_t min_size = std::max(static_cast<int64_t>(BLOCK_SIZE), default_block_size_);
  return alloc_block_buffer(block, data_size, min_size, for_iterator);
}

inline int ObChunkRowStore::dump_one_block(BlockBuffer* item)
{
  int ret = OB_SUCCESS;
  if (item->cur_pos_ <= 0) {
    LOG_WARN("unexpected: dump zero", K(item), K(item->cur_pos_));
  }
  item->block->magic_ = Block::MAGIC;
  if (OB_FAIL(item->get_block()->unswizzling())) {
    LOG_WARN("convert block to copyable failed", K(ret));
  } else if (OB_FAIL(write_file(item->data(), item->capacity()))) {
    LOG_WARN("write block to file failed");
  } else {
    n_block_in_file_++;
    LOG_DEBUG("RowStore Dumpped block", K_(item->block->rows), K_(item->cur_pos), K(item->capacity()));
  }
  return ret;
}

// only clean memory data
int ObChunkRowStore::clean_memory_data(bool reuse)
{
  int ret = OB_SUCCESS;
  Block* cur = blocks_.remove_first();
  BlockBuffer* buf = NULL;
  while (OB_SUCC(ret) && NULL != cur) {
    buf = cur->get_buffer();
    if (!buf->is_empty()) {
      mem_used_ -= buf->mem_size();
      row_cnt_ -= cur->rows();
    }
    if (reuse && buf->mem_size() <= default_block_size_) {
      buf->reuse();
      free_list_.add_last(cur);
    } else {
      mem_hold_ -= buf->mem_size();
      callback_free(buf->mem_size());
      allocator_->free(cur);
    }
    cur = blocks_.remove_first();
  }
  if (mem_used_ != 0 || (!reuse && mem_hold_ != 0) || blocks_.get_size() != 0) {
    SQL_ENG_LOG(WARN,
        "hold mem after clean_memory_data",
        K_(mem_used),
        K(reuse),
        K_(mem_hold),
        K(blocks_.get_size()),
        K(free_list_.get_size()));
  }
  if (OB_SUCC(ret)) {
    cur_blk_ = NULL;
  }
  return ret;
}

int ObChunkRowStore::dump(bool reuse, bool all_dump)
{
  int ret = OB_SUCCESS;
  BlockBuffer* buf = NULL;
  if (!enable_dump_) {
    ret = OB_EXCEED_MEM_LIMIT;
    LOG_DEBUG("ChunkRowStore exceed mem limit and dump is disabled");
  } else {
    int64_t n_block = blocks_.get_size();
    const int64_t org_n_block = n_block;
    Block* cur = nullptr;
    while (OB_SUCC(ret) && 0 < n_block && (all_dump || 1 < n_block)) {
      cur = blocks_.remove_first();
      --n_block;
      if (OB_ISNULL(cur)) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("cur block is null", K(ret));
      } else {
        buf = cur->get_buffer();
        LOG_DEBUG("dumping", K(cur), K(*cur), K(*buf));
        if (!buf->is_empty() && OB_FAIL(dump_one_block(buf))) {
          LOG_WARN("failed to dump block", K(ret));
        }
        if (!buf->is_empty()) {
          mem_used_ -= buf->mem_size();
          dumped_row_cnt_ += cur->rows();
        }
        if (reuse && buf->mem_size() <= default_block_size_) {
          buf->reuse();
          free_list_.add_last(cur);
        } else {
          mem_hold_ -= buf->mem_size();
          callback_free(buf->mem_size());
          allocator_->free(cur);
        }
      }
    }
    if (all_dump && (mem_used_ != 0 || (!reuse && mem_hold_ != 0) || blocks_.get_size() != 0)) {
      LOG_WARN("hold mem after dump",
          K_(mem_used),
          K(reuse),
          K_(mem_hold),
          K(blocks_.get_size()),
          K(free_list_.get_size()),
          K(n_block),
          K(org_n_block),
          K(ret));
    }
    if (OB_SUCC(ret)) {
      if (all_dump) {
        cur_blk_ = NULL;
      }
      if (!has_dumped_) {
        has_dumped_ = (org_n_block != blocks_.get_size());
      }
    }
  }
  return ret;
}

bool ObChunkRowStore::find_block_can_hold(const int64_t size, bool& need_shrink)
{
  bool found = false;
  need_shrink = false;
  if (NULL != cur_blk_ && size <= cur_blk_->get_buffer()->remain()) {
    found = true;
  } else if (free_list_.get_size() > 0 && default_block_size_ >= size) {
    Block* next = free_list_.remove_first();
    LOG_DEBUG("reuse block", K(next), K(*next), K(next->get_buffer()), K(*next->get_buffer()));
    found = true;
    use_block(next);
    blocks_.add_last(next);
    n_blocks_++;
  } else if (mem_limit_ > 0 && mem_hold_ > mem_used_ && mem_hold_ + size > mem_limit_) {
    need_shrink = true;
    LOG_DEBUG("RowStore need shrink", K(size), K(mem_hold_));
  }
  return found;
}

int ObChunkRowStore::switch_block(const int64_t min_size)
{
  int ret = OB_SUCCESS;
  if (!is_inited()) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret));
  } else if (min_size <= 0 || OB_ISNULL(cur_blk_)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(ret), K(min_size));
  } else if (need_dump(min_size) && OB_FAIL(dump(true, true))) {
    if (OB_EXCEED_MEM_LIMIT != ret) {
      LOG_WARN("got error when dump blocks", K(ret));
    }
  } else {
    LOG_DEBUG("RowStore switch block", K(min_size));
    Block* new_block = NULL;
    bool need_shrink = false;
    bool can_find = find_block_can_hold(min_size, need_shrink);
    LOG_DEBUG("RowStore switch block", K(can_find), K(need_shrink), K(min_size));
    if (need_shrink) {
      if (shrink_block(min_size)) {
        LOG_DEBUG("RowStore shrink succ", K(min_size));
      }
    }
    if (!can_find) {  // need alloc new block
      if (OB_FAIL(alloc_block_buffer(new_block, min_size, false))) {
        LOG_WARN("alloc block failed", K(ret), K(min_size), K(new_block));
      }
      if (!can_find && OB_SUCC(ret)) {
        blocks_.add_last(new_block);
        n_blocks_++;
        use_block(new_block);
        LOG_DEBUG("RowStore switch block",
            K(new_block),
            K(*new_block),
            K(new_block->get_buffer()),
            K(n_blocks_),
            K(min_size));
      }
    }
  }
  return ret;
}

int ObChunkRowStore::add_row(const common::ObNewRow& row, StoredRow** stored_row)
{
  int ret = OB_SUCCESS;
  if (!is_inited()) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret));
  } else {
    int64_t bak_projector_size_ = 0;
    int32_t* bak_projector_ = NULL;
    ObNewRow& r = const_cast<ObNewRow&>(row);
    // store project
    if (STORE_MODE::FULL == row_store_mode_ && NULL != row.projector_ && row.projector_size_ > 0) {
      LOG_DEBUG("has projector", K(row.projector_size_), K(row.count_));
      if (OB_ISNULL(projector_)) {
        ObMemAttr attr(tenant_id_, label_, ctx_id_);
        projector_size_ = row.projector_size_;
        const int64_t size = projector_size_ * sizeof(*projector_);
        projector_ = static_cast<int32_t*>(allocator_->alloc(size));
        if (OB_ISNULL(projector_)) {
          ret = OB_ALLOCATE_MEMORY_FAILED;
          LOG_WARN("allocate memory failed", K(ret));
        } else {
          MEMCPY(projector_, row.projector_, size);
        }
      } else if (projector_size_ != row.projector_size_) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("projector size mismatch A", K(ret), K(projector_size_), K(row.projector_size_));
      }
      if (OB_SUCC(ret)) {
        bak_projector_ = r.projector_;
        bak_projector_size_ = r.projector_size_;
        r.projector_size_ = 0;
        r.projector_ = NULL;
      }
    }
    const int64_t row_size = Block::row_store_size(row, row_extend_size_);
    const int64_t min_buf_size = Block::min_buf_size(row_size);
    if (OB_SUCC(ret)) {
      if (NULL == cur_blk_) {
        Block* new_blk = nullptr;
        if (OB_FAIL(alloc_block_buffer(new_blk, min_buf_size, false))) {
          LOG_WARN("alloc block failed", K(ret));
        } else {
          use_block(new_blk);
          blocks_.add_last(cur_blk_);
          n_blocks_++;
        }
      }
      if (OB_SUCC(ret) && row_size > cur_blk_->get_buffer()->remain()) {
        if (OB_FAIL(switch_block(min_buf_size)) && OB_EXCEED_MEM_LIMIT != ret) {
          LOG_WARN("switch block failed", K(ret), K(row));
        }
      }
      if (OB_SUCC(ret)) {
        if (OB_FAIL(cur_blk_->add_row(row, row_size, row_extend_size_, stored_row))) {
          LOG_WARN("add row to block failed", K(ret), K(row), K(row_size));
        } else {
          row_cnt_++;
          if (0 >= col_count_) {
            col_count_ = row.get_count();
          }
        }
      }
    }
    // always keep row unchanged.
    if (NULL != bak_projector_) {
      r.projector_size_ = bak_projector_size_;
      r.projector_ = bak_projector_;
    }
    // if (stored_row != NULL) {
    //   stored_type_ = (*stored_row)->TYPE;
    //  }
  }

  return ret;
}

int ObChunkRowStore::copy_row(const StoredRow* stored_row, ObChunkRowStore* crs)
{
  int ret = OB_SUCCESS;
  if (!is_inited()) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret));
  } else {
    const int64_t row_size = stored_row->row_size_;
    const int64_t min_buf_size = Block::min_buf_size(row_size);
    if (OB_NOT_NULL(crs) && OB_NOT_NULL(crs->projector_) && nullptr == projector_) {
      projector_size_ = crs->projector_size_;
      const int64_t size = projector_size_ * sizeof(*projector_);
      projector_ = static_cast<int32_t*>(allocator_->alloc(size));
      if (OB_ISNULL(projector_)) {
        ret = OB_ALLOCATE_MEMORY_FAILED;
        LOG_WARN("allocate memory failed", K(ret));
      } else {
        MEMCPY(projector_, crs->projector_, size);
      }
    } else if (OB_NOT_NULL(crs) && projector_size_ != crs->projector_size_) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("projector size mismatch", K(ret), K(projector_size_), K(crs->projector_size_));
    }
    if (OB_SUCC(ret)) {
      if (NULL == cur_blk_) {
        Block* new_blk = nullptr;
        if (OB_FAIL(alloc_block_buffer(new_blk, min_buf_size, false))) {
          LOG_WARN("alloc block failed", K(ret));
        } else {
          use_block(new_blk);
          blocks_.add_last(cur_blk_);
          n_blocks_++;
        }
      }
      if (OB_SUCC(ret) && row_size > cur_blk_->get_buffer()->remain()) {
        if (OB_FAIL(switch_block(min_buf_size)) && OB_EXCEED_MEM_LIMIT != ret) {
          LOG_WARN("switch block failed", K(ret), K(*stored_row));
        }
      }
      if (OB_SUCC(ret)) {
        if (OB_FAIL(cur_blk_->copy_row(stored_row))) {
          LOG_WARN("add row to block failed", K(ret), K(*stored_row), K(row_size));
        } else {
          row_cnt_++;
          if (col_count_ <= 0) {
            col_count_ = stored_row->cnt_;
          }
        }
      }
    }
  }

  return ret;
}

int ObChunkRowStore::finish_add_row(bool need_dump)
{
  int ret = OB_SUCCESS;
  int64_t timeout_ms = 0;
  if (free_list_.get_size() > 0) {
    Block* block = free_list_.remove_first();
    while (NULL != block) {
      mem_hold_ -= block->get_buffer()->mem_size();
      callback_free(block->get_buffer()->mem_size());
      allocator_->free(block);
      block = free_list_.remove_first();
    }
    free_list_.reset();
  }
  if (is_file_open()) {
    if (need_dump && OB_FAIL(dump(false, true)) && OB_EXCEED_MEM_LIMIT != ret) {
      LOG_WARN("finish_add_row dump error", K(ret));
    } else if (OB_FAIL(get_timeout(timeout_ms))) {
      LOG_WARN("get timeout failed", K(ret));
    } else if (OB_FAIL(FILE_MANAGER_INSTANCE_V2.sync(io_.fd_, timeout_ms))) {
      LOG_WARN("sync file failed", K(ret), K_(io_.fd), K(timeout_ms));
    }
  } else {
    LOG_DEBUG("finish_add_row no need to dump", K(ret));
  }

  return ret;
}

// add block manually, this block must be initialized by init_block_buffer()
// and must be removed by remove_added_block before ObChunkRowStore<>::reset()
int ObChunkRowStore::add_block(Block* block, bool need_swizzling, bool* added)
{
  int ret = OB_SUCCESS;
  int64_t col_cnt = 0;
  bool need_add = true;

  if (need_swizzling) {
    // for iterate
    if (block->rows_ <= 0) {
      need_add = false;
    } else if (OB_FAIL(block->swizzling(&col_cnt))) {
      LOG_WARN("add block failed", K(block), K(need_swizzling));
    } else if (this->col_count_ != 0 && this->col_count_ != col_cnt) {
      LOG_WARN("add block failed col cnt not match", K(block), K(need_swizzling), K(col_cnt), K_(col_count));
    }
  }

  if (need_add) {
    this->row_cnt_ += block->rows_;
    this->n_blocks_++;
    if (nullptr != added) {
      *added = true;
    }
    blocks_.add_last(block);
    if (NULL == cur_blk_) {
      // cur_blk_ point to the first added block
      cur_blk_ = block;
    }
  }
  return ret;
}

int ObChunkRowStore::append_block(char* buf, int size, bool need_swizzling)
{
  int ret = OB_SUCCESS;
  ObMemAttr attr(tenant_id_, label_, ctx_id_);
  Block* src_block = reinterpret_cast<Block*>(buf);
  int64_t block_data_size = size;
  Block* new_block = nullptr;
  bool added = false;
  int64_t actual_size = block_data_size + sizeof(BlockBuffer);
  if (OB_FAIL(alloc_block_buffer(new_block, actual_size, actual_size, false))) {
    LOG_WARN("failed to alloc block buffer", K(ret));
  } else {
    MEMCPY(new_block->payload_, src_block->payload_, block_data_size - sizeof(Block));
    new_block->rows_ = src_block->rows_;
    BlockBuffer* block_buffer = new_block->get_buffer();
    use_block(new_block);
    if (block_data_size == sizeof(Block)) {
      // empty block, ignore
      free_blk_mem(new_block, block_buffer->mem_size());
    } else if (OB_FAIL(block_buffer->advance(block_data_size - sizeof(Block)))) {
      LOG_WARN("failed to advanced buffer", K(ret));
    } else if (block_buffer->data_size() != block_data_size) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("unexpected status: data size is not match", K(ret), K(block_buffer->data_size()), K(block_data_size));
    } else if (OB_FAIL(add_block(new_block, need_swizzling, &added))) {
      LOG_WARN("fail to add block", K(ret));
    } else {
      LOG_TRACE("trace append block", K(src_block->rows_), K(size));
    }
    if (OB_FAIL(ret) && !added) {
      free_blk_mem(new_block, block_buffer->mem_size());
    }
  }
  return ret;
}

void ObChunkRowStore::remove_added_blocks()
{
  blocks_.reset();
  n_blocks_ = 0;
  row_cnt_ = 0;
}

int ObChunkRowStore::load_next_chunk_blocks(ChunkIterator& it)
{
  int ret = OB_SUCCESS;
  int64_t read_off = 0;
  if (NULL == it.chunk_mem_) {
    if (it.chunk_read_size_ > file_size_) {
      it.chunk_read_size_ = file_size_;
    }
    it.chunk_mem_ = static_cast<char*>(alloc_blk_mem(sizeof(char) * it.chunk_read_size_, true));
    if (OB_ISNULL(it.chunk_mem_)) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_WARN("alloc memory failed", K(ret), K(it.chunk_read_size_));
    }
  } else {
    /* when the last block haven't been read fully,
       copy the head half to the start of chunk-mem and read next chunk after it */
    char* last_blk_end = static_cast<char*>(static_cast<void*>(it.cur_iter_blk_)) + it.cur_iter_blk_->blk_size_;
    char* chunk_end = it.chunk_mem_ + it.chunk_read_size_;
    LOG_DEBUG(
        "load chunk", KP(last_blk_end), K_(it.cur_iter_blk_->blk_size), KP(chunk_end), K(chunk_end - last_blk_end));
    if (chunk_end > last_blk_end) {
      MEMCPY(it.chunk_mem_, last_blk_end, chunk_end - last_blk_end);
      read_off += chunk_end - last_blk_end;
    }
  }

  if (OB_SUCC(ret)) {
    int64_t read_n_blocks = 0;
    int64_t read_size = it.chunk_read_size_ - read_off;
    int64_t chunk_size = it.chunk_read_size_;
    int64_t tmp_read_size = 0;
    it.chunk_n_rows_ = 0;
    if (read_size > (it.file_size_ - it.cur_iter_pos_)) {
      tmp_read_size = read_size;
      read_size = it.file_size_ - it.cur_iter_pos_;
      chunk_size = read_size + read_off;
    }

    if (0 == read_size) {
      // ret end
      int tmp_ret = OB_SUCCESS;
      if (OB_SUCCESS != (tmp_ret = read_file(it.chunk_mem_ + read_off, tmp_read_size, it.cur_iter_pos_))) {
        LOG_WARN("read blk info from file failed", K(tmp_ret), K_(it.cur_iter_pos));
      }
      if (OB_ITER_END != tmp_ret) {
        LOG_WARN("unexpected status", K(ret), K(tmp_ret));
      } else {
        ret = OB_ITER_END;
      }
    } else if (OB_FAIL(read_file(it.chunk_mem_ + read_off, read_size, it.cur_iter_pos_))) {
      LOG_WARN("read blk info from file failed", K(ret), K_(it.cur_iter_pos));
    } else {
      int64_t cur_pos = 0;
      Block* block = reinterpret_cast<Block*>(it.chunk_mem_);
      Block* prev_block = block;
      do {
        if (!block->magic_check()) {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("StoreRow load block magic check failed", K(ret), K(it), K(cur_pos), K(chunk_size));
        } else if (block->blk_size_ <= chunk_size - cur_pos) {
          prev_block->next_ = block;
          cur_pos += block->blk_size_;
          prev_block = block;
          block = reinterpret_cast<Block*>(it.chunk_mem_ + cur_pos);
          read_n_blocks++;
          if (prev_block->blk_size_ == 0 || prev_block->rows_ == 0) {
            ret = OB_INNER_STAT_ERROR;
            LOG_WARN("read file failed", K(ret), K(prev_block->blk_size_), K(read_n_blocks), K(cur_pos));
          } else if (OB_FAIL(prev_block->swizzling(NULL))) {
            LOG_WARN("swizzling failed after read block from file", K(ret), K(it), K(read_n_blocks));
          } else {
            it.chunk_n_rows_ += prev_block->rows_;
          }
        } else {
          break;
        }
      } while (OB_SUCC(ret) && cur_pos < (chunk_size - BlockBuffer::HEAD_SIZE));

      if (OB_SUCC(ret)) {
        prev_block->next_ = NULL;
        it.cur_iter_blk_ = reinterpret_cast<Block*>(it.chunk_mem_);
        it.cur_iter_pos_ += read_size;
        it.cur_chunk_n_blocks_ = read_n_blocks;
        it.cur_nth_blk_ += read_n_blocks;
        if (0 == read_n_blocks) {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("failed to chunk read blocks",
              K(read_n_blocks),
              K(it),
              K(read_off),
              K(it.cur_iter_pos_),
              K(read_size),
              K(chunk_size),
              K(cur_pos),
              K(ret));
        } else {
          LOG_TRACE("chunk read blocks succ:",
              K(read_n_blocks),
              K(it),
              K(read_off),
              K(it.cur_iter_pos_),
              K(read_size),
              K(chunk_size),
              K(cur_pos),
              K(it.cur_nth_blk_));
        }
      }
    }
  }
  if (OB_ITER_END == ret) {
    it.set_read_file_iter_end();
    if (nullptr != it.chunk_mem_) {
      callback_free(it.chunk_read_size_);
      allocator_->free(it.chunk_mem_);
    }
    it.chunk_mem_ = nullptr;
    it.cur_iter_blk_ = nullptr;
  }
  return ret;
}

/* get next block from BlockItemList(when all rows in mem) or read from file
 * and let it.cur_iter_blk_ point to the new block
 */
int ObChunkRowStore::load_next_block(ChunkIterator& it)
{
  int ret = OB_SUCCESS;
  if (it.file_size_ != file_size_) {
    it.reset_cursor(file_size_);
  }
  if (!is_inited()) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret));
  } else if (it.cur_nth_blk_ < -1 || it.cur_nth_blk_ >= n_blocks_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("row should be saved", K(ret), K_(it.cur_nth_blk), K_(n_blocks));
  } else if (is_file_open() && !it.read_file_iter_end()) {
    if (it.chunk_read_size_ > 0 && it.chunk_read_size_ >= this->max_blk_size_) {
      if (OB_FAIL(load_next_chunk_blocks(it))) {
        LOG_WARN("RowStore iter load next chunk blocks failed", K(ret));
      }
    } else {
      /* read from file:
       * read a block of min_size first, if the actual size is lager, then alloc a new block and copy
       * the previous block to the head of it, then read the rest from file
       * */
      int64_t block_size = INT64_MAX == min_blk_size_
                               ? it.chunk_read_size_
                               : (it.chunk_read_size_ > min_blk_size_ ? it.chunk_read_size_ : min_blk_size_);
      int64_t read_size = block_size - sizeof(BlockBuffer);
      if (it.chunk_mem_ != NULL) {
        // only for unittest
        allocator_->free(it.chunk_mem_);
        it.chunk_mem_ = NULL;
        it.cur_iter_blk_ = NULL;
        callback_free(it.chunk_read_size_);
      }
      if (NULL == it.cur_iter_blk_) {
        // for the first time, it.cur_iter_blk_ need to be freed by Iterator
        if (OB_FAIL(alloc_block_buffer(it.cur_iter_blk_, block_size, true))) {
          LOG_WARN("alloc block failed", K(ret));
        } else {
          it.cur_iter_blk_buf_ = it.cur_iter_blk_->get_buffer();
          LOG_DEBUG("alloc a block for reading",
              K(ret),
              K(it.cur_iter_blk_),
              K(it.cur_iter_blk_->get_buffer()->capacity()),
              K(block_size));
        }
      } else {
        if (it.cur_iter_blk_buf_ == NULL) {
          ret = OB_INNER_STAT_ERROR;
          LOG_WARN("it.cur_iter_blk_buf_ is NULL", K(ret), K_(it.cur_iter_blk_buf));
        } else {
          it.cur_iter_blk_buf_->reuse();
          LOG_DEBUG("StoreRow reuse block",
              K(it.cur_iter_blk_buf_->capacity()),
              K(it.cur_iter_blk_->get_buffer()),
              K_(it.cur_iter_blk_buf),
              K(block_size),
              K(read_size));
        }
      }

      if (OB_SUCC(ret)) {
        int64_t blk_cap = it.cur_iter_blk_buf_->capacity();

        LOG_DEBUG("RowStore need read file",
            K(it.cur_iter_blk_buf_->capacity()),
            K(it.cur_iter_blk_->blk_size_),
            K(block_size),
            K_(file_size),
            K_(it.cur_iter_pos),
            K_(it.cur_iter_blk));
        // read a normal size of Block first
        if (OB_FAIL(read_file(it.cur_iter_blk_, read_size, it.cur_iter_pos_))) {
          if (OB_ITER_END != ret) {
            LOG_WARN("read blk info from file failed", K(ret), K_(it.cur_iter_pos));
          }
        } else if (!it.cur_iter_blk_->magic_check()) {
          ret = OB_ERR_UNEXPECTED;
          LOG_WARN("StoreRow load block magic check failed", K(ret), K(it.cur_iter_blk_));
        } else if (it.cur_iter_blk_->blk_size_ > read_size) {
          LOG_DEBUG("RowStore blk size larger than min", K(read_size), K(it.cur_iter_blk_->blk_size_));
          Block* blk_head = it.cur_iter_blk_;
          int64_t ac_size = it.cur_iter_blk_->blk_size_;
          int64_t pre_size = it.cur_iter_blk_buf_->mem_size();
          LOG_DEBUG("RowStore need read file", K(ac_size), K(blk_head), K(block_size));
          if (read_size + it.cur_iter_pos_ > file_size_) {
            ret = OB_ITER_END;
            LOG_WARN("RowStore iter end unexpected", K(ret), K(it.cur_iter_pos_), K(file_size_), K(read_size));
          } else if (blk_cap < ac_size) {
            // need to alloc new blk to hold data
            it.cur_iter_blk_ = nullptr;
            if (OB_FAIL(alloc_block_buffer(it.cur_iter_blk_, ac_size + sizeof(BlockBuffer), true)) ||
                ac_size != it.cur_iter_blk_->get_buffer()->capacity()) {
              LOG_WARN("alloc block failed", K(ret), K(ac_size), K(it.cur_iter_blk_));
              allocator_->free(blk_head);
              callback_free(pre_size);
            } else {
              it.cur_iter_blk_buf_ = it.cur_iter_blk_->get_buffer();
              MEMCPY(it.cur_iter_blk_, blk_head, read_size);
              allocator_->free(blk_head);
              callback_free(pre_size);
            }
          }
          if (OB_FAIL(ret)) {
          } else if (OB_FAIL(read_file(static_cast<void*>(it.cur_iter_blk_->payload_ + read_size - sizeof(Block)),
                         ac_size - read_size,
                         it.cur_iter_pos_ + read_size))) {
            if (OB_ITER_END != ret) {
              LOG_WARN("read blk info from file failed", K(ret), K_(it.cur_iter_pos));
            }
          } else {
            read_size = ac_size;
          }
        }

        if (OB_FAIL(ret)) {
          if (OB_ITER_END == ret) {
            it.set_read_file_iter_end();
          }
        } else if (OB_FAIL(it.cur_iter_blk_->swizzling(NULL))) {
          LOG_WARN("swizzling failed after read block from file", K(ret), K(it));
        } else if (read_size <= 0 || it.cur_iter_blk_->blk_size_ == 0 || it.cur_iter_blk_->rows_ == 0) {
          ret = OB_INNER_STAT_ERROR;
          LOG_WARN("read file failed", K(ret), K(read_size), K(block_size), K(it.cur_iter_blk_->blk_size_));
        }
      }
      if (OB_SUCC(ret)) {
        it.cur_iter_pos_ += read_size;
        it.cur_nth_blk_++;
        it.cur_chunk_n_blocks_ = 1;
        it.cur_iter_blk_->next_ = NULL;
        it.chunk_n_rows_ = it.cur_iter_blk_->rows_;
        LOG_TRACE(
            "StoreRow read file succ", K(read_size), K_(it.cur_iter_blk), K_(*it.cur_iter_blk), K_(it.cur_iter_pos));
      } else {
        // first read disk data then read memory data, so it must free cur_iter_blk_
        if (NULL != it.cur_iter_blk_) {
          callback_free(it.cur_iter_blk_->get_buffer()->mem_size());
          allocator_->free(it.cur_iter_blk_);
          it.cur_iter_blk_ = NULL;
          it.cur_iter_blk_buf_ = nullptr;
        }
      }
    }
  } else {
    it.set_read_file_iter_end();
  }
  // when it's iter_end from disk or disk is no data
  // then read data from in memory
  if (OB_FAIL(ret) && OB_ITER_END != ret) {
  } else if (!is_file_open() || it.read_file_iter_end()) {
    ret = OB_SUCCESS;
    if (OB_ISNULL(blocks_.get_first())) {
      it.set_read_mem_iter_end();
      ret = OB_ITER_END;
      LOG_TRACE("RowStore no more blocks",
          K(ret),
          K_(it.cur_nth_blk),
          K_(n_blocks),
          K(n_block_in_file_),
          K(io_.fd_),
          K(it.iter_end_flag_));
    } else if (nullptr == blocks_.get_first()) {
      ret = OB_ITER_END;
    } else {
      // all in mem or read blocks in mem at first
      it.cur_iter_blk_ = blocks_.get_first();
      LOG_DEBUG("RowStore got block in mem", K_(it.cur_iter_blk), K_(*it.cur_iter_blk));
      it.cur_chunk_n_blocks_ = blocks_.get_size();
      it.cur_nth_blk_ += it.cur_chunk_n_blocks_;
      it.chunk_n_rows_ = this->get_row_cnt_in_memory();
      LOG_TRACE("trace read in memoery data", K(ret));
      if (it.cur_nth_blk_ != n_blocks_ - 1) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("unexpected status: last chunk data", K(it.cur_nth_blk_), K(n_blocks_), K(blocks_.get_size()), K(ret));
      }
    }
  }
  return ret;
}

int ObChunkRowStore::get_store_row(RowIterator& it, const StoredRow*& sr)
{
  int ret = OB_SUCCESS;
  if (!is_inited() || NULL == it.cur_iter_blk_) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret), K_(it.cur_iter_blk));
  } else {
    if (OB_UNLIKELY(!it.cur_blk_has_next())) {
      if (it.cur_iter_blk_->get_next() != NULL) {
        it.cur_iter_blk_ = it.cur_iter_blk_->get_next();
        it.cur_row_in_blk_ = 0;
        it.cur_pos_in_blk_ = 0;
        it.cur_nth_block_++;
      } else if (it.cur_nth_block_ != it.n_blocks_ - 1) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("load block failed",
            K(ret),
            K_(it.cur_row_in_blk),
            K_(it.cur_pos_in_blk),
            K_(it.cur_nth_block),
            K_(it.n_blocks),
            K(lbt()));
      } else {
        ret = OB_ITER_END;
      }
    }
    if (OB_SUCC(ret)) {
      if (OB_FAIL(it.cur_iter_blk_->get_store_row(it.cur_pos_in_blk_, sr))) {
        LOG_WARN("get row from block failed", K(ret), K_(it.cur_row_in_blk), K(*it.cur_iter_blk_));
      } else {
        it.cur_row_in_blk_++;
      }
    }
  }
  return ret;
}

////////////////////////////////////////////////////////////////
ObChunkRowStore::RowIterator::RowIterator()
    : store_(NULL), cur_iter_blk_(NULL), cur_row_in_blk_(0), cur_pos_in_blk_(0), n_blocks_(0), cur_nth_block_(0)
{}

ObChunkRowStore::RowIterator::RowIterator(ObChunkRowStore* row_store)
    : store_(row_store), cur_iter_blk_(NULL), cur_row_in_blk_(0), cur_pos_in_blk_(0), n_blocks_(0), cur_nth_block_(0)
{}

int ObChunkRowStore::RowIterator::init(ChunkIterator* chunk_it)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(chunk_it) || !chunk_it->is_valid()) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(chunk_it));
  } else {
    store_ = chunk_it->store_;
    row_.projector_ = NULL;
    row_.projector_size_ = 0;
  }
  return ret;
}

ObChunkRowStore::ChunkIterator::ChunkIterator()
    : store_(NULL),
      cur_iter_blk_(NULL),
      cur_iter_blk_buf_(NULL),
      cur_nth_blk_(-1),
      cur_chunk_n_blocks_(0),
      cur_iter_pos_(0),
      file_size_(0),
      chunk_read_size_(0),
      chunk_mem_(NULL),
      chunk_n_rows_(0),
      iter_end_flag_(IterEndState::PROCESSING)
{}

int ObChunkRowStore::ChunkIterator::init(ObChunkRowStore* store, int64_t chunk_read_size)
{
  int ret = OB_SUCCESS;
  store_ = store;
  chunk_read_size_ = chunk_read_size;
  return ret;
}

ObChunkRowStore::ChunkIterator::~ChunkIterator()
{
  reset_cursor(0);
}

void ObChunkRowStore::ChunkIterator::reset_cursor(const int64_t file_size)
{
  file_size_ = file_size;

  if (chunk_mem_ != NULL) {
    store_->allocator_->free(chunk_mem_);
    chunk_mem_ = NULL;
    cur_iter_blk_ = NULL;
    store_->callback_free(chunk_read_size_);
    if (read_file_iter_end()) {
      LOG_ERROR("unexpect status: chunk mem is allocated, but don't free");
    }
  }
  if (!read_file_iter_end() && cur_iter_blk_ != NULL) {
    if (cur_iter_pos_ > 0 && NULL != store_) {
      if (nullptr != cur_iter_blk_buf_) {
        store_->callback_free(cur_iter_blk_buf_->mem_size());
      }
      store_->allocator_->free(cur_iter_blk_);
    }
  }
  cur_iter_blk_buf_ = nullptr;
  cur_iter_blk_ = nullptr;
  cur_nth_blk_ = -1;
  cur_iter_pos_ = 0;
  iter_end_flag_ = IterEndState::PROCESSING;
}

void ObChunkRowStore::ChunkIterator::reset()
{
  reset_cursor(0);
  chunk_read_size_ = 0;
  chunk_n_rows_ = 0;
}

int ObChunkRowStore::ChunkIterator::load_next_chunk(RowIterator& it)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(!is_valid())) {
    ret = OB_NOT_INIT;
    LOG_WARN("ChunkIterator not init", K(ret));
  } else if (!has_next_chunk()) {
    ret = OB_ITER_END;
  } else {
    if (chunk_read_size_ > 0) {
      cur_iter_blk_ = it.cur_iter_blk_;
    }
    if (OB_FAIL(store_->load_next_block(*this))) {
      LOG_WARN("load block failed", K(ret), K(*this));
    } else {
      it.store_ = store_;
      it.cur_iter_blk_ = cur_iter_blk_;
      it.cur_pos_in_blk_ = 0;
      it.cur_row_in_blk_ = 0;
      it.n_blocks_ = cur_chunk_n_blocks_;
      it.cur_nth_block_ = 0;
    }
  }
  return ret;
}

int ObChunkRowStore::Iterator::init(ObChunkRowStore* store, int64_t chunk_read_size)
{
  reset();
  switch (store->row_store_mode_) {
    case STORE_MODE::WITHOUT_PROJECTOR:
      (Iterator::convert_row_with_obj_fun_) = &Iterator::convert_to_row_with_obj;
      Iterator::convert_row_fun_ = &Iterator::convert_to_row;
      break;
    case STORE_MODE::FULL:
      Iterator::convert_row_with_obj_fun_ = &Iterator::convert_to_row_full_with_obj;
      Iterator::convert_row_fun_ = &Iterator::convert_to_row_full;
      break;
  }
  int ret = OB_SUCCESS;
  if (OB_FAIL(chunk_it_.init(store, chunk_read_size)) || OB_FAIL(row_it_.init(&chunk_it_))) {
    LOG_WARN("chunk iterator or row iterator init failed", K(ret));
  }
  return ret;
}

int ObChunkRowStore::Iterator::get_next_row(ObNewRow& row)
{
  int ret = OB_SUCCESS;
  const StoredRow* sr = NULL;
  if (OB_FAIL(get_next_row(sr))) {
    if (OB_ITER_END != ret) {
      LOG_WARN("get next stored row failed", K(ret));
    }
  } else if (OB_FAIL((this->*convert_row_with_obj_fun_)(row_it_, row, sr))) {
    LOG_WARN("convert row failed", K(ret), K_(chunk_it), K_(row_it));
  }
  return ret;
}

int ObChunkRowStore::Iterator::get_next_row(ObNewRow*& row)
{
  int ret = OB_SUCCESS;
  const StoredRow* sr = NULL;
  if (OB_FAIL(get_next_row(sr))) {
    if (OB_ITER_END != ret) {
      LOG_WARN("get next stored row failed", K(ret));
    }
  } else if (OB_FAIL((this->*convert_row_fun_)(row_it_, row, sr))) {
    LOG_WARN("convert row failed", K(ret), K_(chunk_it), K_(row_it));
  }
  return ret;
}

int ObChunkRowStore::Iterator::get_next_row(const StoredRow*& sr)
{
  int ret = OB_SUCCESS;
  if (!start_iter_) {
    if (OB_FAIL(chunk_it_.load_next_chunk(row_it_))) {
      if (OB_ITER_END != ret) {
        LOG_WARN("Iterator load chunk failed", K(ret));
      }
    } else {
      start_iter_ = true;
    }
  }
  if (OB_SUCC(ret) && OB_FAIL(row_it_.get_next_row(sr))) {
    if (OB_ITER_END == ret) {
      if (OB_FAIL(chunk_it_.load_next_chunk(row_it_))) {
        if (OB_ITER_END != ret) {
          LOG_WARN("Iterator load chunk failed", K(ret));
        }
      } else if (OB_FAIL(row_it_.get_next_row(sr))) {
        LOG_WARN("get next row failed", K(ret));
      }
    }
  }
  return ret;
}

int ObChunkRowStore::RowIterator::get_next_row(const StoredRow*& sr)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(store_->get_store_row(*this, sr))) {
    if (OB_ITER_END != ret) {
      LOG_WARN("get store row failed", K(ret), K_(cur_nth_block), K_(cur_pos_in_blk), K_(cur_row_in_blk));
    }
  }
  return ret;
}

/* from StoredRow to NewRow */
int ObChunkRowStore::RowIterator::convert_to_row(common::ObNewRow& row, const StoredRow* sr)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(sr)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("NULL store row returned", K(ret));
  } else if (row.count_ < sr->cnt_) {
    ret = OB_BUF_NOT_ENOUGH;
    OB_LOG(WARN, "column buffer count is not enough", K_(row.count), K_(sr->cnt));
  } else {
    // do not overwite row.count_
    // row.count_ = sr->cnt_;
    MEMCPY(row.cells_, sr->cells(), sizeof(ObObj) * sr->cnt_);
  }
  return ret;
}

/* from StoredRow to NewRow */
int ObChunkRowStore::RowIterator::convert_to_row_full(common::ObNewRow& row, const StoredRow* sr)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(sr)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("NULL store row returned", K(ret));
  } else if (row.count_ < sr->cnt_) {
    ret = OB_BUF_NOT_ENOUGH;
    OB_LOG(WARN, "column buffer count is not enough", K_(row.count), K(store_->get_col_cnt()));
  } else {
    // do not overwite row.count_
    // row.count_ = sr->cnt_;
    MEMCPY(row.cells_, sr->cells(), sizeof(ObObj) * sr->cnt_);
    MEMCPY(row.projector_, store_->projector_, sizeof(*store_->projector_) * sr->cnt_);
    row.projector_size_ = store_->projector_size_;
  }
  return ret;
}

/* from StoredRow to NewRow */
int ObChunkRowStore::RowIterator::convert_to_row(common::ObNewRow*& row, const StoredRow* sr)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(sr)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("NULL store row returned", K(ret));
  } else {
    // do not overwite row.count_
    // row.count_ = sr->cnt_;
    row_.projector_ = NULL;
    row_.projector_size_ = 0;
    row_.count_ = sr->cnt_;
    row_.cells_ = const_cast<ObObj*>(sr->cells());
    row = &row_;
  }
  return ret;
}

/* from StoredRow to NewRow */
int ObChunkRowStore::RowIterator::convert_to_row_full(common::ObNewRow*& row, const StoredRow* sr)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(sr)) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("NULL store row returned", K(ret));
  } else {
    row_.projector_ = store_->projector_;
    row_.projector_size_ = store_->projector_size_;
    row_.count_ = sr->cnt_;
    row_.cells_ = const_cast<ObObj*>(sr->cells());
    row = &row_;
  }
  return ret;
}

int ObChunkRowStore::get_timeout(int64_t& timeout_ms)
{
  int ret = OB_SUCCESS;
  const int64_t timeout_us = THIS_WORKER.get_timeout_remain();
  if (timeout_us / 1000 <= 0) {
    ret = OB_TIMEOUT;
    LOG_WARN("query is timeout", K(ret), K(timeout_us));
  } else {
    timeout_ms = timeout_us / 1000;
  }
  return ret;
}

int ObChunkRowStore::alloc_dir_id()
{
  int ret = OB_SUCCESS;
  if (-1 == io_.dir_id_ && OB_FAIL(ObChunkStoreUtil::alloc_dir_id(io_.dir_id_))) {
    LOG_WARN("allocate file directory failed", K(ret));
  }
  return ret;
}

int ObChunkRowStore::write_file(void* buf, int64_t size)
{
  int ret = OB_SUCCESS;
  int64_t timeout_ms = 0;
  if (!is_inited()) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret));
  } else if (size < 0 || (size > 0 && NULL == buf)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(size), KP(buf));
  } else if (OB_FAIL(get_timeout(timeout_ms))) {
    LOG_WARN("get timeout failed", K(ret));
  } else {
    if (!is_file_open()) {
      if (-1 == io_.dir_id_) {
        ret = OB_ERR_UNEXPECTED;
        LOG_WARN("temp file dir id is not init", K(ret), K(io_.dir_id_));
      } else if (OB_FAIL(FILE_MANAGER_INSTANCE_V2.open(io_.fd_, io_.dir_id_))) {
        LOG_WARN("open file failed", K(ret));
      } else {
        file_size_ = 0;
        io_.tenant_id_ = tenant_id_;
        io_.io_desc_.category_ =
            GCONF._large_query_io_percentage.get_value() > 0 ? common::LARGE_QUERY_IO : common::USER_IO;
        io_.io_desc_.wait_event_no_ = ObWaitEventIds::ROW_STORE_DISK_WRITE;
        LOG_TRACE("open file success", K_(io_.fd), K_(io_.dir_id));
      }
    }
    ret = E(EventTable::EN_8) ret;
  }
  if (OB_SUCC(ret) && size > 0) {
    set_io(size, static_cast<char*>(buf));
    if (OB_FAIL(FILE_MANAGER_INSTANCE_V2.write(io_, timeout_ms))) {
      LOG_WARN("write to file failed", K(ret), K_(io), K(timeout_ms));
    }
  }
  if (OB_SUCC(ret)) {
    file_size_ += size;
    if (nullptr != callback_) {
      callback_->dumped(size);
    }
  }
  return ret;
}

int ObChunkRowStore::read_file(void* buf, const int64_t size, const int64_t offset)
{
  int ret = OB_SUCCESS;
  int64_t timeout_ms = 0;
  if (!is_inited()) {
    ret = OB_NOT_INIT;
    LOG_WARN("not init", K(ret));
  } else if (offset < 0 || size < 0 || (size > 0 && NULL == buf)) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(size), K(offset), KP(buf));
  } else if (OB_FAIL(get_timeout(timeout_ms))) {
    LOG_WARN("get timeout failed", K(ret));
  }

  if (OB_SUCC(ret) && size > 0) {
    this->set_io(size, static_cast<char*>(buf));
    io_.io_desc_.category_ =
        GCONF._large_query_io_percentage.get_value() > 0 ? common::LARGE_QUERY_IO : common::USER_IO;
    io_.io_desc_.wait_event_no_ = ObWaitEventIds::ROW_STORE_DISK_READ;
    blocksstable::ObTmpFileIOHandle handle;
    if (OB_FAIL(FILE_MANAGER_INSTANCE_V2.pread(io_, offset, timeout_ms, handle))) {
      if (OB_ITER_END != ret) {
        LOG_WARN("read form file failed", K(ret), K(io_), K(offset), K(timeout_ms));
      }
    } else if (handle.get_data_size() != size) {
      ret = OB_INNER_STAT_ERROR;
      LOG_WARN("read data less than expected", K(ret), K(io_), "read_size", handle.get_data_size());
    }
  }
  return ret;
}

bool ObChunkRowStore::need_dump(int64_t extra_size)
{
  bool dump = false;
  if (!GCONF.is_sql_operator_dump_enabled()) {
    // no dump
  } else if (mem_limit_ > 0) {
    if (mem_used_ + extra_size > mem_limit_) {
      dump = true;
    }
  }
  return dump;
}

int ObChunkStoreUtil::alloc_dir_id(int64_t& dir_id)
{
  int ret = OB_SUCCESS;
  dir_id = 0;
  if (OB_FAIL(FILE_MANAGER_INSTANCE_V2.alloc_dir(dir_id))) {
    LOG_WARN("allocate file directory failed", K(ret));
  }
  return ret;
}

int ObChunkRowStore::Block::gen_unswizzling_payload(char* unswizzling_payload, uint32 size)
{
  int ret = OB_SUCCESS;
  int64_t cur_pos = 0;
  uint32_t i = 0;
  if (OB_ISNULL(unswizzling_payload) || size < blk_size_) {
    ret = OB_INVALID_ARGUMENT;
    LOG_WARN("invalid argument", K(size), K(blk_size_), KP(unswizzling_payload));
  } else {
    MEMCPY(unswizzling_payload, payload_, blk_size_);
  }
  while (OB_SUCC(ret) && cur_pos < blk_size_ && i < rows_) {
    StoredRow* sr = reinterpret_cast<StoredRow*>(unswizzling_payload + cur_pos);
    sr->unswizzling(payload_ + cur_pos);
    cur_pos += sr->row_size_;
    i++;
  }
  return ret;
}

OB_DEF_SERIALIZE(ObChunkRowStore)
{
  int ret = OB_SUCCESS;
  if (enable_dump_) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("chunk row store not support serialize if enable dump", K(ret));
  }
  LST_DO_CODE(
      OB_UNIS_ENCODE, tenant_id_, ctx_id_, mem_limit_, default_block_size_, col_count_, enable_dump_, row_extend_size_);
  int64_t count = blocks_.get_size();
  OB_UNIS_ENCODE(count);
  if (OB_SUCC(ret)) {
    Block* block = blocks_.get_first();
    while (NULL != block && OB_SUCC(ret)) {
      OB_UNIS_ENCODE(block->blk_size_);
      OB_UNIS_ENCODE(block->rows_);
      if (OB_SUCC(ret)) {
        if (buf_len - pos < block->blk_size_) {
          ret = OB_SIZE_OVERFLOW;
        } else if (OB_FAIL(block->gen_unswizzling_payload(buf + pos, block->blk_size_))) {
          LOG_WARN("fail to unswizzling", K(ret));
        } else {
          pos += block->blk_size_;
          block = block->get_next();
        }
      }
    }
  }

  return ret;
}

OB_DEF_DESERIALIZE(ObChunkRowStore)
{
  int ret = OB_SUCCESS;
  LST_DO_CODE(OB_UNIS_DECODE, tenant_id_, ctx_id_, mem_limit_);
  if (!is_inited()) {
    if (OB_FAIL(init(mem_limit_, tenant_id_, ctx_id_, "ChunkRowDE", false /*enable_dump*/))) {
      LOG_WARN("fail to init chunk row store", K(ret));
    }
  }
  LST_DO_CODE(OB_UNIS_DECODE, default_block_size_, col_count_, enable_dump_, row_extend_size_);
  if (OB_SUCC(ret)) {
    Block* block = NULL;
    int64_t blk_size = 0;
    int64_t row_cnt = 0;
    int64_t blk_cnt = 0;
    OB_UNIS_DECODE(blk_cnt);
    for (int64_t i = 0; i < blk_cnt && OB_SUCC(ret); ++i) {
      OB_UNIS_DECODE(blk_size);
      OB_UNIS_DECODE(row_cnt);
      if (OB_FAIL(ret)) {
      } else if (OB_FAIL(alloc_block_buffer(block, blk_size, false /*for_iterator*/))) {
        LOG_WARN("alloc block failed", K(ret), K(blk_size), KP(block));
      } else {
        MEMCPY(block->payload_, buf + pos, blk_size);
        block->blk_size_ = blk_size;
        block->rows_ = row_cnt;
        pos += blk_size;
        if (OB_FAIL(add_block(block, true /*+need_swizzling*/))) {
          LOG_WARN("fail to add block", K(ret));
        }
      }
    }
  }
  return ret;
}

OB_DEF_SERIALIZE_SIZE(ObChunkRowStore)
{
  int64_t len = 0;
  LST_DO_CODE(OB_UNIS_ADD_LEN,
      tenant_id_,
      ctx_id_,
      mem_limit_,
      default_block_size_,
      col_count_,
      enable_dump_,
      row_extend_size_);
  int64_t count = blocks_.get_size();
  OB_UNIS_ADD_LEN(count);
  const Block* block = blocks_.get_first();
  while (NULL != block) {
    OB_UNIS_ADD_LEN(block->blk_size_);
    OB_UNIS_ADD_LEN(block->rows_);
    len += block->blk_size_;
    block = block->get_next();
  }

  return len;
}

}  // end namespace sql
}  // end namespace oceanbase
#endif  // OCEANBASE_BASIC_OB_CHUNK_ROW_STORE_CPP_
