/**
 * 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 COMMON
#include "common/row/ob_row_store.h"
#include "common/row/ob_row_util.h"
#include "lib/utility/utility.h"
namespace oceanbase {
namespace common {

////////////////////////////////////////////////////////////////
struct ObRowStore::BlockInfo {
  explicit BlockInfo(int64_t block_size)
      : magic_(0xabcd4444abcd4444), next_(NULL), prev_(NULL), curr_data_pos_(0), block_size_(block_size)
  {}
  OB_INLINE int64_t get_remain_size() const
  {
    return block_size_ - curr_data_pos_ - sizeof(BlockInfo);
  }
  OB_INLINE int64_t get_remain_size_for_read(int64_t pos) const
  {
    return curr_data_pos_ - pos;
  }
  OB_INLINE char* get_buffer()
  {
    return data_ + curr_data_pos_;
  }
  OB_INLINE const char* get_buffer(int64_t pos) const
  {
    return data_ + pos;
  }
  OB_INLINE const char* get_buffer_head() const
  {
    return data_;
  }
  OB_INLINE void advance(const int64_t length)
  {
    curr_data_pos_ += length;
  }
  OB_INLINE int64_t get_curr_data_pos() const
  {
    return curr_data_pos_;
  }
  OB_INLINE void set_curr_data_pos(int64_t pos)
  {
    curr_data_pos_ = pos;
  }
  OB_INLINE BlockInfo* get_next_block()
  {
    return next_;
  }
  OB_INLINE const BlockInfo* get_next_block() const
  {
    return next_;
  }
  OB_INLINE void set_next_block(BlockInfo* blk)
  {
    next_ = blk;
  };
  OB_INLINE bool is_empty() const
  {
    return (0 == curr_data_pos_);
  }
  OB_INLINE void set_block_size(const int64_t block_size)
  {
    block_size_ = block_size;
  };
  OB_INLINE int64_t get_block_size() const
  {
    return block_size_;
  };

  int append_row(
      const ObIArray<int64_t>& reserved_columns, const ObNewRow& row, int64_t payload, StoredRow*& stored_row);
  int append_row_by_copy(
      const ObIArray<int64_t>& reserved_columns, const ObNewRow& row, int64_t payload, StoredRow*& stored_row);
  friend class ObRowStore::BlockList;

private:
#ifdef __clang__
  int64_t magic_ [[gnu::unused]];
#else
  int64_t magic_;
#endif
  BlockInfo* next_;
  BlockInfo* prev_;
  /**
   * cur_data_pos_ must be set when BlockInfo deserialized
   */
  int64_t curr_data_pos_;
  int64_t block_size_;
  char data_[0];
};

int ObRowStore::BlockInfo::append_row(
    const ObIArray<int64_t>& reserved_columns, const ObNewRow& row, int64_t payload, StoredRow*& stored_row)
{
  int ret = OB_SUCCESS;
  // store header
  const int64_t reserved_columns_count = reserved_columns.count();
  const int64_t reserved_cells_size = get_reserved_cells_size(reserved_columns_count);
  stored_row = reinterpret_cast<StoredRow*>(get_buffer());

  advance(reserved_cells_size);
  if (OB_ISNULL(stored_row)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "get invalid stored row", K(ret));
  } else if (OB_UNLIKELY(curr_data_pos_ > block_size_ - SIZEOF(*this))) {
    ret = OB_BUF_NOT_ENOUGH;
    OB_LOG(DEBUG, "buffer size is not enough for head, ", K(ret), K(curr_data_pos_), K(block_size_), K(SIZEOF(*this)));
  } else {
    stored_row->reserved_cells_count_ = static_cast<int32_t>(reserved_columns_count);
    stored_row->payload_ = payload;
    // store row data
    ObObj cell_clone;
    ObCellWriter cell_writer;
    if (OB_FAIL(cell_writer.init(get_buffer(), get_remain_size(), DENSE))) {
      // empty
      OB_LOG(WARN, "init cell writer failed", K(ret));
    } else {
      int32_t reserved_count = 0;
      int64_t count = row.get_count();
      for (int64_t i = 0; OB_SUCC(ret) && i < count; ++i) {
        // honor the projector
        const ObObj& cell = row.get_cell(i);
        if (cell.is_ext() && ObActionFlag::OP_END_FLAG == cell.get_ext()) {
          ret = OB_ERR_UNEXPECTED;
          OB_LOG(WARN, "append row is invalid", K(row));
        } else if (OB_FAIL(cell_writer.append(OB_INVALID_ID, cell, &cell_clone))) {
          if (OB_BUF_NOT_ENOUGH != ret) {
            OB_LOG(WARN, "failed to append cell", K(ret));
          }
        }
        // whether reserve this cell
        for (int64_t j = 0; OB_SUCC(ret) && j < reserved_columns_count; ++j) {
          const int64_t index = reserved_columns.at(j);
          if (index == i) {
            stored_row->reserved_cells_[j] = cell_clone;
            ++reserved_count;
            // should not break when ORDER BY c1, c1 DESC, ...
            // If the break goes out here, reserved_count will be less than reserved_columns_count
            // break;
          }
        }  // end for j
      }    // end for i
      if (OB_SUCC(ret)) {
        if (reserved_columns_count != reserved_count) {
          OB_LOG(WARN,
              "reserved columns count not equeal to actual add count",
              K(reserved_columns_count),
              K(reserved_count));
          ret = OB_ERR_UNEXPECTED;
        }
      }
      if (OB_SUCC(ret)) {
        if (OB_FAIL(cell_writer.row_finish())) {
          if (OB_BUF_NOT_ENOUGH != ret) {
            OB_LOG(WARN, "failed to append cell", K(ret));
          }
        } else {
          // success
          stored_row->compact_row_size_ = static_cast<int32_t>(cell_writer.size());
          advance(cell_writer.size());
          if (OB_UNLIKELY(curr_data_pos_ > block_size_ - SIZEOF(*this))) {
            ret = OB_ERR_UNEXPECTED;
            OB_LOG(ERROR,
                "fail to append row, overwrite the head",
                K(ret),
                K_(curr_data_pos),
                K_(block_size),
                K(SIZEOF(*this)));
          }
        }
      }
    }
  }
  if (OB_BUF_NOT_ENOUGH == ret) {
    // rollback header
    advance(-reserved_cells_size);
    if (OB_UNLIKELY(0 > get_curr_data_pos())) {
      ret = OB_ERR_SYS;
      OB_LOG(WARN, "out of memory range, ", K(ret), K_(block_size), K_(curr_data_pos), K(reserved_cells_size));
    }
  }
  return ret;
}

int ObRowStore::BlockInfo::append_row_by_copy(
    const ObIArray<int64_t>& reserved_columns, const ObNewRow& row, int64_t payload, StoredRow*& stored_row)
{
  int ret = OB_SUCCESS;
  // store header
  stored_row = reinterpret_cast<StoredRow*>(get_buffer());
  if (OB_UNLIKELY(row.projector_size_ > 0)) {
    ret = OB_NOT_SUPPORTED;
    OB_LOG(WARN, "row with projector is not supported", K(ret), K(row.projector_size_));
  } else if (OB_ISNULL(stored_row)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "get invalid stored row", K(ret));
  } else {
    const int64_t header_size = get_reserved_cells_size(reserved_columns.count());
    const int64_t new_row_size = sizeof(ObNewRow) + row.get_deep_copy_size();

    if (OB_UNLIKELY(get_remain_size() < header_size + new_row_size)) {
      ret = OB_BUF_NOT_ENOUGH;
      OB_LOG(DEBUG,
          "buffer size is not enough for the row",
          K(ret),
          K(curr_data_pos_),
          K(block_size_),
          K(SIZEOF(*this)),
          K(header_size),
          K(new_row_size));
    } else {
      stored_row->reserved_cells_count_ = static_cast<int32_t>(reserved_columns.count());
      stored_row->payload_ = payload;
      stored_row->compact_row_size_ = static_cast<int32_t>(new_row_size);
      advance(header_size);

      ObNewRow* row_clone = new (get_buffer()) ObNewRow();
      int64_t pos = sizeof(ObNewRow);
      if (OB_FAIL(row_clone->deep_copy(row, get_buffer(), new_row_size, pos))) {
        ret = OB_ERR_UNEXPECTED;
        LOG_ERROR("failed to deep copy row", K(ret), K(new_row_size), K(pos));
      } else if (new_row_size != pos) {
        ret = OB_ERR_UNEXPECTED;
        LOG_ERROR("size != pos", K(ret), K(new_row_size), K(pos));
      } else {
        advance(new_row_size);
        for (int64_t i = 0; OB_SUCC(ret) && i < reserved_columns.count(); ++i) {
          int64_t index = reserved_columns.at(i);
          if (OB_UNLIKELY(index >= row.count_)) {
            ret = OB_ERR_UNEXPECTED;
            LOG_WARN("invalid index", K(index), K(row.count_), K(ret));
          } else {
            stored_row->reserved_cells_[i] = row_clone->cells_[reserved_columns.at(i)];
          }
        }
      }
    }
  }
  return ret;
}

////////////////////////////////////////////////////////////////
ObRowStore::BlockList::BlockList() : first_(NULL), last_(NULL), count_(0), used_mem_size_(0){};

void ObRowStore::BlockList::reset()
{
  first_ = NULL;
  last_ = NULL;
  count_ = 0;
  used_mem_size_ = 0;
}

int ObRowStore::BlockList::add_last(BlockInfo* block)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(block)) {
    ret = OB_INVALID_ARGUMENT;
    OB_LOG(WARN, "invalid argument", K(ret), K(block));
  } else {
    block->next_ = NULL;
    block->prev_ = last_;
    if (OB_ISNULL(last_)) {
      if (OB_ISNULL(first_)) {
        first_ = block;
        last_ = block;
      } else {
        ret = OB_ERR_UNEXPECTED;
        OB_LOG(WARN, "invalid block list", K(ret), K_(last), K_(first));
      }
    } else {
      last_->next_ = block;
      last_ = block;
    }
  }
  if (OB_SUCC(ret)) {
    used_mem_size_ += block->get_block_size();
    ++count_;
  }
  return ret;
}

ObRowStore::BlockInfo* ObRowStore::BlockList::pop_last()
{
  BlockInfo* block = last_;
  if (NULL != block) {
    if (NULL != block->prev_) {
      block->prev_->next_ = NULL;
      last_ = block->prev_;
    } else {
      // the only one
      first_ = NULL;
      last_ = NULL;
    }
    block->next_ = NULL;
    block->prev_ = NULL;
    --count_;
    used_mem_size_ -= block->get_block_size();
  }
  return block;
}

////////////////////////////////////////////////////////////////
ObRowStore::Iterator::Iterator() : row_store_(NULL), cur_iter_block_(NULL), cur_iter_pos_(0)
{}

ObRowStore::Iterator::Iterator(const ObRowStore* row_store)
    : row_store_(row_store), cur_iter_block_(NULL), cur_iter_pos_(0)
{}

// Why not reset row_store_, which does not satisfy the semantics of reset, and renamed to reuse?
void ObRowStore::Iterator::reset()
{
  // don't reset row_store_
  cur_iter_block_ = NULL;
  cur_iter_pos_ = 0;
}

void ObRowStore::Iterator::set_invalid()
{
  row_store_ = NULL;
  cur_iter_block_ = NULL;
  cur_iter_pos_ = 0;
}

int ObRowStore::Iterator::get_next_stored_row(StoredRow*& stored_row)
{
  int ret = OB_SUCCESS;
  stored_row = NULL;
  if (NULL == row_store_) {
    ret = OB_ERR_UNEXPECTED;
    LOG_WARN("row store is NULL", K(ret));
  } else {
    if (NULL == cur_iter_block_) {
      cur_iter_block_ = row_store_->blocks_.get_first();
      cur_iter_pos_ = 0;
    }
  }

  if (OB_FAIL(ret)) {
  } else if (OB_FAIL(next_iter_pos(cur_iter_block_, cur_iter_pos_))) {
    if (OB_ITER_END != ret) {
      OB_LOG(WARN, "failed to get next pos", K(ret));
    }
  } else if (OB_ISNULL(cur_iter_block_)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "invalid cur iter block", K(ret), K(cur_iter_block_));
  } else {
    stored_row =
        const_cast<StoredRow*>(reinterpret_cast<const StoredRow*>(cur_iter_block_->get_buffer_head() + cur_iter_pos_));
    // next
    cur_iter_pos_ +=
        (row_store_->get_reserved_cells_size(stored_row->reserved_cells_count_) + stored_row->compact_row_size_);
  }  // end else
  return ret;
}

int ObRowStore::Iterator::get_next_row(ObNewRow& row)
{
  return get_next_row(row, NULL, NULL);
}

int ObRowStore::Iterator::get_next_row(ObNewRow& row, common::ObString* compact_row, StoredRow** r_stored_row)
{
  int ret = OB_SUCCESS;
  StoredRow* stored_row = NULL;
  if (OB_UNLIKELY(row.is_invalid())) {
    ret = OB_INVALID_ARGUMENT;
    OB_LOG(WARN, "input argument row is invalid", K(row.count_), K(row.cells_));
  } else if (OB_ISNULL(row_store_)) {
    OB_LOG(WARN, "row_store_ is NULL, should not reach here");
    ret = OB_ERR_UNEXPECTED;
  } else if (OB_UNLIKELY(row.count_ < row_store_->get_col_count())) {
    ret = OB_BUF_NOT_ENOUGH;
    OB_LOG(WARN, "column buffer count is not enough", K_(row.count), K(row_store_->get_col_count()));
  } else if (OB_FAIL(get_next_stored_row(stored_row))) {
    // do nothing
  } else if (OB_ISNULL(stored_row)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "stored_row should not be NULL", K(ret));
  } else if (OB_FAIL(ObRowUtil::convert(stored_row->get_compact_row(), row))) {
    OB_LOG(WARN, "fail to convert compact row to ObRow", K(ret));
  } else {
    if (NULL != r_stored_row) {
      *r_stored_row = stored_row;
    }
    if (NULL != compact_row) {
      *compact_row = stored_row->get_compact_row();
    }
  }
  return ret;
}

int ObRowStore::Iterator::get_next_row(ObNewRow*& row, StoredRow** r_stored_row)
{
  int ret = OB_SUCCESS;
  StoredRow* stored_row = NULL;
  if (OB_ISNULL(row_store_)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(ERROR, "row_store_ is NULL", K(ret));
  } else if (row_store_->use_compact_row()) {
    ret = OB_NOT_SUPPORTED;
    OB_LOG(WARN, "this row store uses compact_row and you should not use this iterator", K(ret));
  } else if (OB_FAIL(get_next_stored_row(stored_row))) {
    // do nothing
  } else if (OB_ISNULL(stored_row)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "stored_row should not be NULL", K(ret));
  } else {
    row = reinterpret_cast<ObNewRow*>(stored_row->get_compact_row_ptr());
    if (NULL != r_stored_row) {
      *r_stored_row = stored_row;
    }
  }
  return ret;
}

int ObRowStore::Iterator::next_iter_pos(const BlockInfo*& iter_block, int64_t& iter_pos)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(iter_block)) {
    ret = OB_ITER_END;
  } else if (0 >= iter_block->get_remain_size_for_read(iter_pos)) {
    if (OB_ISNULL(iter_block->get_next_block())) {
      // the last block
      ret = OB_ITER_END;
    } else {
      // next block
      iter_block = iter_block->get_next_block();
      iter_pos = 0;
    }
  } else {
  }
  return ret;
}

bool ObRowStore::Iterator::has_next() const
{
  return (NULL != row_store_ &&
          !((cur_iter_block_ == NULL) || (cur_iter_block_ == row_store_->blocks_.get_last() &&
                                             cur_iter_block_->get_remain_size_for_read(cur_iter_pos_) <= 0)));
}

////////////////////////////////////////////////////////////////
ObRowStore::ObRowStore(ObIAllocator& alloc, const lib::ObLabel& label /*=ObModIds::OB_SQL_ROW_STORE*/,
    const uint64_t tenant_id /*=OB_INVALID_TENANT_ID*/, bool use_compact_row /*= true*/)
    : inner_alloc_(),
      reserved_columns_(alloc),
      blocks_(),
      block_size_(NORMAL_BLOCK_SIZE),
      data_size_(0),
      row_count_(0),
      col_count_(0),
      last_last_row_size_(-1),
      last_row_size_(-1),
      tenant_id_(tenant_id),
      label_(label),
      ctx_id_(ObCtxIds::DEFAULT_CTX_ID),
      is_read_only_(false),
      has_big_block_(false),
      use_compact_row_(use_compact_row),
      alloc_(alloc),
      pre_project_buf_(NULL),
      pre_alloc_block_(false)
{
  inner_alloc_.set_label(label);
}
ObRowStore::ObRowStore(const lib::ObLabel& label /*=ObModIds::OB_SQL_ROW_STORE*/,
    const uint64_t tenant_id /*=OB_INVALID_TENANT_ID*/, bool use_compact_row /*= true*/)
    : inner_alloc_(),
      reserved_columns_(inner_alloc_),
      blocks_(),
      block_size_(NORMAL_BLOCK_SIZE),
      data_size_(0),
      row_count_(0),
      col_count_(0),
      last_last_row_size_(-1),
      last_row_size_(-1),
      tenant_id_(tenant_id),
      label_(label),
      ctx_id_(ObCtxIds::DEFAULT_CTX_ID),
      is_read_only_(false),
      has_big_block_(false),
      use_compact_row_(use_compact_row),
      alloc_(inner_alloc_),
      pre_project_buf_(NULL),
      pre_alloc_block_(false)
{
  inner_alloc_.set_label(label);
}

ObRowStore::~ObRowStore()
{
  clear_rows();
}

int ObRowStore::add_reserved_column(int64_t index)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(0 > index)) {
    ret = OB_INVALID_ARGUMENT;
    OB_LOG(WARN, "invalid argument", K(index));
  } else if (OB_FAIL(reserved_columns_.push_back(index))) {
    OB_LOG(WARN, "failed to push into array", K(ret));
  }
  return ret;
}

void ObRowStore::reset()
{
  clear_rows();
  reserved_columns_.reset();
  tenant_id_ = OB_SERVER_TENANT_ID;
  pre_alloc_block_ = false;
}

// method for ObAggregateFunction::prepare()
// prepare need to reuse ObRowStore for WRITE,
// it needs to reuse reserved_columns_ which should not be cleared
void ObRowStore::clear_rows()
{
  data_size_ = 0;
  row_count_ = 0;
  col_count_ = 0;
  last_last_row_size_ = -1;
  last_row_size_ = -1;
  is_read_only_ = false;
  has_big_block_ = false;

  // free all blocks
  BlockInfo* block = blocks_.get_first();
  while (NULL != block) {
    BlockInfo* next = block->get_next_block();
    block->~BlockInfo();
    alloc_.free(block);
    block = next;
  }
  blocks_.reset();
}

/*
 * Use with care, unreasonable use will cause memory leaks,
 * Decide whether to use this function according to alloc_ and the memory management of your own usage scenario,
 * This function keeps the newly allocated block and deletes other blocks, the effect is similar to reuse
 */
void ObRowStore::reuse_hold_one_block()
{
  data_size_ = 0;
  row_count_ = 0;
  col_count_ = 0;
  last_last_row_size_ = -1;
  last_row_size_ = -1;
  is_read_only_ = false;
  has_big_block_ = false;

  BlockInfo* hold_block = blocks_.get_first();
  if (NULL != hold_block) {
    BlockInfo* block = hold_block->get_next_block();
    while (NULL != block) {
      BlockInfo* next = block->get_next_block();
      block->~BlockInfo();
      alloc_.free(block);
      block = next;
    }
    blocks_.reset();
    hold_block->set_next_block(NULL);
    hold_block->set_curr_data_pos(0);
    blocks_.add_last(hold_block);
  } else {
    blocks_.reset();
  }
}

int ObRowStore::assign_block(char* buf, int64_t block_size)
{
  int ret = OB_SUCCESS;
  BlockInfo* block = (BlockInfo*)(buf);
  if (OB_ISNULL(block)) {
    ret = OB_INVALID_ARGUMENT;
    OB_LOG(WARN, "invalid argument", K(ret));
  } else {
    block = new (block) BlockInfo(block_size);
    pre_alloc_block_ = true;
    block_size_ = block_size;
    ret = blocks_.add_last(block);
  }
  return ret;
}

ObRowStore::BlockInfo* ObRowStore::new_block(int64_t block_size)
{
  BlockInfo* block = NULL;
  // normalize block size
  if (block_size > BIG_BLOCK_SIZE) {
    block_size = OB_MAX_ROW_LENGTH_IN_MEMTABLE;
  } else if (block_size > NORMAL_BLOCK_SIZE) {
    block_size = BIG_BLOCK_SIZE;
  } else {
    block_size = NORMAL_BLOCK_SIZE;
  }
  // make sure all memory allocated under the right tenant
  ObMemAttr attr(tenant_id_, label_, ctx_id_);
  block = static_cast<BlockInfo*>(alloc_.alloc(block_size, attr));
  if (OB_ISNULL(block)) {
    OB_LOG(WARN, "no memory");
  } else {
    block = new (block) BlockInfo(block_size);
    if (!has_big_block_ && block_size == BIG_BLOCK_SIZE) {
      has_big_block_ = true;
    }
  }
  return block;
}

// If there are already big blocks, then all new blocks are big.
ObRowStore::BlockInfo* ObRowStore::new_block()
{
  BlockInfo* blk = NULL;
  if (OB_LIKELY(!has_big_block_)) {  // use config BLOCK size
    if (OB_ISNULL(blk = new_block(block_size_))) {
      OB_LOG(WARN, "failed to new block", K_(block_size));
    }
  } else {  // use BIG_BLOCK
    if (OB_ISNULL(blk = new_block(BIG_BLOCK_SIZE))) {
      OB_LOG(WARN, "failed to new big block", K(blk));
    }
  }
  return blk;
}

int ObRowStore::add_row(const ObNewRow& row, const StoredRow*& stored_row, int64_t payload, bool by_projector)
{
  int ret = OB_SUCCESS;
  if (!by_projector) {
    // Remove row's projector
    ObNewRow tmp_row;
    tmp_row.cells_ = row.cells_;
    tmp_row.count_ = row.count_;
    ret = add_row_by_projector(tmp_row, stored_row, payload);
  } else {
    ret = add_row_by_projector(row, stored_row, payload);
  }
  return ret;
}

int ObRowStore::add_row(const ObNewRow& row, bool by_projector)
{
  const StoredRow* stored_row = NULL;  // value ignored
  return add_row(row, stored_row, 0, by_projector);
}

int ObRowStore::add_row_by_projector(const ObNewRow& row, const StoredRow*& stored_row, int64_t payload)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(is_read_only_)) {
    ret = OB_ERR_READ_ONLY;
    OB_LOG(WARN, "read only ObRowStore, not allowed to add row", K(ret));
  } else if (OB_UNLIKELY(0 < col_count_) && OB_UNLIKELY(row.get_count() != col_count_)) {
    ret = OB_INVALID_ARGUMENT;
    OB_LOG(WARN, "all rows should have the same columns", K(col_count_), K(row.get_count()));
  } else {
    stored_row = NULL;
    const int32_t reserved_columns_count = static_cast<int32_t>(reserved_columns_.count());
    int64_t min_need_size = get_compact_row_min_size(row.get_count()) + get_reserved_cells_size(reserved_columns_count);
    BlockInfo* block = NULL;
    if (OB_ISNULL(blocks_.get_last()) || blocks_.get_last()->get_remain_size() <= min_need_size) {
      if (pre_alloc_block_) {
        ret = OB_SIZE_OVERFLOW;
      } else if (OB_ISNULL(block = new_block())) {
        ret = OB_ALLOCATE_MEMORY_FAILED;
        OB_LOG(ERROR, "failed to new block", K(ret));
      }
    } else {
      block = blocks_.get_last();
    }
    if (OB_SUCC(ret) && OB_ISNULL(block)) {
      ret = OB_ERR_UNEXPECTED;
      OB_LOG(WARN, "fail to get valid block", K(ret), K(block));
    }
    // try three times:
    // first, try to append into the current block
    // if failed, then try to append into a new NORMAL block
    // if still failed, then append into a new BIG block
    // if the row is larger than the BIG block, it should fail anyway
    int64_t retry = 0;
    StoredRow* stored = NULL;
    while (OB_SUCC(ret) && retry <= 3) {
      if (use_compact_row_) {
        ret = block->append_row(reserved_columns_, row, payload, stored);
      } else {
        if (NULL != row.projector_) {
          if (OB_UNLIKELY(NULL == pre_project_buf_)) {
            if (OB_FAIL(init_pre_project_row(row.get_count()))) {
              OB_LOG(WARN, "fail to init pre project row", K(ret), K(block));
            }
          }
          if (OB_SUCC(ret)) {
            if (OB_FAIL(pre_project(row))) {
              OB_LOG(WARN, "fail to pre-project row", K(ret), K(block));
            } else {
              ret = block->append_row_by_copy(reserved_columns_, pre_project_row_, payload, stored);
            }
          }
        } else {
          ret = block->append_row_by_copy(reserved_columns_, row, payload, stored);
        }
      }
      if (OB_FAIL(ret)) {
        if (OB_BUF_NOT_ENOUGH == ret) {
          if (pre_alloc_block_) {
            ret = OB_SIZE_OVERFLOW;
          } else {
            ret = OB_SUCCESS;
            ++retry;
            if (block->is_empty()) {
              // a fresh block is not large enough, free it and new a BIG one
              int64_t big_block_size =
                  block->get_block_size() >= BIG_BLOCK_SIZE ? OB_MAX_ROW_LENGTH_IN_MEMTABLE : BIG_BLOCK_SIZE;
              if (block == blocks_.get_last()) {  // to be robust
                blocks_.pop_last();
              }
              alloc_.free(block);
              if (OB_ISNULL(block = new_block(big_block_size))) {
                ret = OB_ALLOCATE_MEMORY_FAILED;
                OB_LOG(ERROR, "failed to new block", K(ret));
              }
            } else if (OB_ISNULL(block = new_block())) {  // normal case
              ret = OB_ALLOCATE_MEMORY_FAILED;
              OB_LOG(ERROR, "failed to new block", K(ret));
            }
          }
        } else {
          OB_LOG(WARN, "failed to append row", K(ret));
        }
      } else if (OB_ISNULL(stored)) {
        ret = OB_ERR_UNEXPECTED;
        OB_LOG(WARN, "get invalid stored row", K(ret), K(stored));
      } else {
        if (block != blocks_.get_last()) {
          // new block
          ret = blocks_.add_last(block);
        }
        stored_row = stored;
        ++row_count_;
        last_last_row_size_ = last_row_size_;
        last_row_size_ = stored_row->compact_row_size_ + get_reserved_cells_size(stored_row->reserved_cells_count_);
        data_size_ += last_row_size_;
        if (0 >= col_count_) {
          col_count_ = row.get_count();
        }
        break;  // done
      }
    }  // end while
    if (3 < retry) {
      ret = OB_ERR_UNEXPECTED;
      OB_LOG(ERROR, "unexpected branch");
    }
  }
  return ret;
}

int ObRowStore::init_pre_project_row(int64_t count)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(NULL == pre_project_buf_)) {
    if (OB_UNLIKELY(count <= 0)) {
      ret = OB_INVALID_ARGUMENT;
      OB_LOG(WARN, "invalid count", K(count), K(ret));
    } else {
      ObMemAttr attr(tenant_id_, label_, ctx_id_);
      pre_project_buf_ = static_cast<ObObj*>(alloc_.alloc(count * sizeof(ObObj), attr));
      if (OB_UNLIKELY(NULL == pre_project_buf_)) {
        ret = OB_ERR_UNEXPECTED;
        OB_LOG(WARN, "invalid count", K(count), K(ret));
      } else {
        memset(pre_project_buf_, 0, count * sizeof(ObObj));
        pre_project_row_.assign(pre_project_buf_, count);
      }
    }
  }
  return ret;
}

int ObRowStore::pre_project(const ObNewRow& row)
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(row.is_invalid())) {
    ret = OB_INVALID_ARGUMENT;
    OB_LOG(WARN, "invalid row", K(row), K(ret));
  } else {
    int64_t count = row.get_count();
    for (int64_t i = 0; i < count; ++i) {
      pre_project_buf_[i] = row.get_cell(i);
    }
  }
  return ret;
}

int ObRowStore::rollback_last_row()
{
  int ret = OB_SUCCESS;
  // only support add_row->rollback->add_row->rollback->add_row->add_row->rollback
  // NOT support  ..->rollback->rollback->...
  BlockInfo* last = blocks_.get_last();
  if (0 >= last_row_size_ ||
      (0 < last_row_size_ && 0 >= last_last_row_size_ && 1 < row_count_) /*already rollbacked once*/) {
    ret = OB_NOT_SUPPORTED;
    OB_LOG(WARN, "only one row could be rollback after called add_row() once");
  } else if (OB_ISNULL(last)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "last blockinfo is null ,can't rollback", K(ret), K(last));
  } else {
    last->advance(-last_row_size_);
    if (OB_UNLIKELY(0 > last->get_curr_data_pos())) {
      ret = OB_ERR_SYS;
      OB_LOG(ERROR,
          "out of memory range, ",
          K(ret),
          "block_size",
          last->get_block_size(),
          "curr_data_pos",
          last->get_curr_data_pos(),
          K_(last_row_size));
    } else {
      --row_count_;
      if (last->is_empty()) {
        blocks_.pop_last();
        alloc_.free(last);
        last = NULL;
      }
      last_row_size_ = last_last_row_size_;
      last_last_row_size_ = -1;
    }
  }
  return ret;
}

int ObRowStore::get_last_row(const StoredRow*& stored_row) const
{
  int ret = OB_SUCCESS;
  if (0 >= last_row_size_) {
    ret = OB_ENTRY_NOT_EXIST;
    OB_LOG(WARN, "has no last stored row");
  } else if (OB_ISNULL(blocks_.get_last())) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "fail to get last row", K(ret), K(blocks_.get_last()));
  } else if (OB_UNLIKELY(0 > blocks_.get_last()->get_curr_data_pos() - last_row_size_)) {
    ret = OB_ERR_UNEXPECTED;
    OB_LOG(WARN, "wrong last row store size", K(ret), K(last_row_size_), K(blocks_.get_last()->get_curr_data_pos()));
  } else {
    stored_row = reinterpret_cast<const StoredRow*>(
        blocks_.get_last()->get_buffer(blocks_.get_last()->get_curr_data_pos() - last_row_size_));
  }
  return ret;
}

ObRowStore::Iterator ObRowStore::begin() const
{
  return Iterator(this);
}

void ObRowStore::dump() const
{
  int ret = OB_SUCCESS;
  OB_LOG(DEBUG, "DUMP row store:", K(*this));
  StoredRow* stored_row = NULL;
  ObNewRow row;
  static const int64_t INLINE_OBJS_SIZE = 16;
  ObObj inline_objs[INLINE_OBJS_SIZE];
  row.cells_ = inline_objs;
  char* buf = NULL;
  if (INLINE_OBJS_SIZE < col_count_) {
    ObMemAttr attr(tenant_id_, label_);
    buf = static_cast<char*>(ob_malloc(sizeof(ObObj) * col_count_, attr));
    if (OB_ISNULL(buf)) {
      OB_LOG(WARN, "failed to alloc memory for objs");
      ret = OB_ALLOCATE_MEMORY_FAILED;
    } else {
      row.cells_ = new (buf) ObObj[col_count_];
    }
  }
  if (OB_SUCC(ret)) {
    row.count_ = static_cast<int32_t>(col_count_);
    Iterator it = begin();
    int64_t row_count = 1;
    while (OB_SUCCESS == (ret = it.get_next_row(row, NULL, &stored_row))) {
      OB_LOG(DEBUG, "DUMP row", K(row_count), K(*stored_row));
      OB_LOG(DEBUG, "DUMP row data", K(row_count), K(row));
      ++row_count;
    }
  }
  if (NULL != buf) {
    ob_free(buf);
  }
}

int32_t ObRowStore::get_copy_size() const
{
  return static_cast<int32_t>(data_size_ + sizeof(BlockInfo) * blocks_.get_block_count() + get_meta_size());
}

ObRowStore* ObRowStore::clone(char* buffer, int64_t buffer_length) const
{
  ObRowStore* copy = NULL;
  int64_t used_length = 0;
  if (OB_ISNULL(buffer)) {
    OB_LOG(WARN, "buffer is NULL");
  } else if (0 < reserved_columns_.count()) {
    OB_LOG(ERROR, "row store with reserved columns should not be cloned");
  } else if (OB_UNLIKELY(buffer_length < get_meta_size())) {
    OB_LOG(WARN, "invalid buffer length", K(buffer_length), K(get_meta_size()));
  } else {
    copy = new (buffer) ObRowStore();
    if (OB_ISNULL(copy)) {
      OB_LOG(WARN, "fail to new ObRowStore", K(copy));
    } else {
      used_length += get_meta_size();
      copy->is_read_only_ = true;
      copy->block_size_ = block_size_;
      copy->data_size_ = data_size_;
      copy->row_count_ = row_count_;
      copy->col_count_ = col_count_;
      copy->tenant_id_ = tenant_id_;
      copy->label_ = label_;

      buffer += get_meta_size();
      const BlockInfo* bip = blocks_.get_first();
      BlockInfo* tmp = NULL;
      while (NULL != bip) {
        const int64_t sz = sizeof(BlockInfo) + bip->get_curr_data_pos();
        used_length += sz;
        if (OB_UNLIKELY(buffer_length < used_length)) {
          copy = NULL;
          OB_LOG(WARN, "invalid buffer length", K(buffer_length), K(used_length));
          break;
        }
        MEMCPY(buffer, bip, sz);
        tmp = new (buffer) BlockInfo(sz);
        if (OB_ISNULL(tmp)) {
          OB_LOG(WARN, "fail to new BlockInfo");
          copy = NULL;
          break;
        } else {
          tmp->set_curr_data_pos(bip->get_curr_data_pos());
          int64_t tmp_ret = OB_SUCCESS;
          if (OB_SUCCESS != (tmp_ret = copy->blocks_.add_last(tmp))) {
            OB_LOG(WARN, "fail to add last to block", K(tmp_ret));
            copy = NULL;
            break;
          } else {
            buffer += sz;
            bip = bip->get_next_block();
          }
        }
      }
    }
  }
  return copy;
}
// @todo improve performance, copy by block
int ObRowStore::assign(const ObRowStore& other_store)
{
  int ret = OB_SUCCESS;
  ObRowStore::Iterator row_store_it = other_store.begin();
  ObNewRow cur_row;
  int64_t col_count = other_store.get_col_count();
  ObObj* cell = NULL;
  clear_rows();
  tenant_id_ = other_store.tenant_id_;
  use_compact_row_ = other_store.use_compact_row_;

  if (OB_FAIL(set_col_count(col_count))) {
    OB_LOG(WARN, "fail to set rowstore columns count", K(ret));
  } else if (OB_ISNULL(cell = static_cast<ObObj*>(alloca(sizeof(ObObj) * col_count)))) {
    OB_LOG(WARN, "fail to alloc obj array", K(ret));
  } else {
    cur_row.cells_ = cell;
    cur_row.count_ = col_count;
    while (true) {
      if (OB_FAIL(row_store_it.get_next_row(cur_row))) {
        if (OB_ITER_END == ret) {
          ret = OB_SUCCESS;
        } else {
          OB_LOG(WARN, "fail to get next row", K(ret));
        }
        break;
      } else if (OB_FAIL(add_row(cur_row))) {
        OB_LOG(WARN, "fail to add row", K(ret));
        break;
      }
    }
  }
  return ret;
}

int ObRowStore::adjust_row_cells_reference()
{
  int ret = OB_SUCCESS;
  // If row compact is not used, the complete data structure of ObNewRow is stored in the data block.
  // The serialization of row_store is the complete serialization of the records on the data block
  // And there is a pointer in ObNewRow, so the value of the pointer becomes illegal after deserialization
  // So if row compact is used, the cells_ pointer in ObNewRow needs to be adjusted after deserialization
  // The adjustment rule is that the memory of ObNewRow and obj cells_ are arranged adjacently, so you only need to move
  // the row ptr forward by sizeof(ObNewRow). Is the value of obj cells_
  if (OB_UNLIKELY(!use_compact_row_)) {
    ObNewRow* cur_row = NULL;
    StoredRow* stored_row = NULL;
    ObRowStore::Iterator row_store_it = begin();
    while (OB_SUCC(ret)) {
      if (OB_FAIL(row_store_it.get_next_stored_row(stored_row))) {
        if (OB_ITER_END != ret) {
          LOG_WARN("get next stored row from row store failed", K(ret));
        }
      } else {
        int64_t pos = 0;
        char* compact_row = reinterpret_cast<char*>(stored_row->get_compact_row_ptr());
        if (OB_FAIL(ObNewRow::construct(compact_row, stored_row->compact_row_size_, pos, cur_row))) {
          LOG_WARN("construct row failed", K(ret), K(stored_row->compact_row_size_), K(pos));
        }
      }
    }
    if (OB_ITER_END == ret) {
      ret = OB_SUCCESS;
    }
  }
  return ret;
}

DEFINE_SERIALIZE(ObRowStore)
{
  int ret = OB_SUCCESS;
  int64_t count = blocks_.get_block_count();
  if (OB_FAIL(serialization::encode_vi64(buf, buf_len, pos, count))) {
    OB_LOG(WARN, "failed to serialize", K(ret), KP(buf), K(buf_len), K(pos));
  } else {
    const BlockInfo* block = blocks_.get_first();

    while (NULL != block && OB_SUCC(ret)) {
      // serialize data_buf_size
      if (OB_FAIL(serialization::encode_vi64(buf, buf_len, pos, block->get_curr_data_pos()))) {
        OB_LOG(WARN, "fail to serialize data_buffer_size", K(ret), KP(buf), K(buf_len), K(pos));
      } else {
        // serialize block data
        if (buf_len - pos < block->get_curr_data_pos()) {
          ret = OB_SIZE_OVERFLOW;
        } else {
          MEMCPY(buf + pos, block->get_buffer_head(), block->get_curr_data_pos());
          pos += block->get_curr_data_pos();
          // serialize next block
          block = block->get_next_block();
        }
      }
    }  // end while
  }

  OB_UNIS_ENCODE(block_size_);
  OB_UNIS_ENCODE(row_count_);
  OB_UNIS_ENCODE(col_count_);
  OB_UNIS_ENCODE(is_read_only_);
  OB_UNIS_ENCODE(last_row_size_);
  OB_UNIS_ENCODE(tenant_id_);
  return ret;
}

DEFINE_DESERIALIZE(ObRowStore)
{
  int ret = OB_SUCCESS;
  int64_t old_pos = pos;
  int64_t block_size = 0;
  int64_t data_buffer_size = 0;
  int64_t block_count = 0;
  BlockInfo* block = NULL;
  if (OB_FAIL(serialization::decode_vi64(buf, data_len, pos, &block_count))) {
    OB_LOG(WARN, "failed to serialize", K(ret), KP(buf), K(data_len), K(pos));
  } else {
    reuse();
    is_read_only_ = true;
    for (int64_t i = 0; i < block_count && OB_SUCC(ret); ++i) {
      if (OB_FAIL(serialization::decode_vi64(buf, data_len, pos, &data_buffer_size))) {
        OB_LOG(WARN, "failed to deserialize data_buffer_size", K(ret), KP(buf), K(data_len), K(pos));
      } else {
        block_size = data_buffer_size + sizeof(BlockInfo);
        if (OB_ISNULL(block = new_block(block_size))) {
          ret = OB_ALLOCATE_MEMORY_FAILED;
          OB_LOG(ERROR, "fail to allocate new block", K(ret), K(block_size));
        } else if (block->get_remain_size() >= data_buffer_size) {
          // copy data to store
          MEMCPY(block->get_buffer(), buf + pos, data_buffer_size);
          block->advance(data_buffer_size);
          pos += data_buffer_size;
          data_size_ += data_buffer_size;
          if (OB_FAIL(blocks_.add_last(block))) {
            OB_LOG(WARN, "fail to add last to block", K(ret));
          }
        } else {
          ret = OB_BUF_NOT_ENOUGH;
          OB_LOG(WARN, "block is not enough, ", K(ret), "remain_size", block->get_remain_size(), K(data_buffer_size));
        }
      }
    }  // end for
  }

  OB_UNIS_DECODE(block_size_);
  OB_UNIS_DECODE(row_count_);
  OB_UNIS_DECODE(col_count_);
  OB_UNIS_DECODE(is_read_only_);
  OB_UNIS_DECODE(last_row_size_);
  OB_UNIS_DECODE(tenant_id_);
  if (OB_SUCC(ret)) {
    if (OB_FAIL(adjust_row_cells_reference())) {
      LOG_WARN("adjust row cells reference failed", K(ret));
    }
  }
  if (OB_FAIL(ret)) {
    pos = old_pos;
  }
  return ret;
}

DEFINE_GET_SERIALIZE_SIZE(ObRowStore)
{
  int64_t count = blocks_.get_block_count();
  int64_t len = serialization::encoded_length_vi64(count);
  const BlockInfo* block = blocks_.get_first();
  while (NULL != block) {
    len += serialization::encoded_length_vi64(block->get_curr_data_pos());
    len += block->get_curr_data_pos();
    block = block->get_next_block();
  }
  OB_UNIS_ADD_LEN(block_size_);
  OB_UNIS_ADD_LEN(row_count_);
  OB_UNIS_ADD_LEN(col_count_);
  OB_UNIS_ADD_LEN(is_read_only_);
  OB_UNIS_ADD_LEN(last_row_size_);
  OB_UNIS_ADD_LEN(tenant_id_);
  return len;
}
}  // namespace common
}  // namespace oceanbase
