/**
 * 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 SHARE
#include "lib/stat/ob_session_stat.h"
#include "share/ob_core_table_proxy.h"
#include "share/ob_dml_sql_splicer.h"

#include <gtest/gtest.h>
#include "schema/db_initializer.h"

namespace oceanbase {
namespace share {
using namespace common;
using namespace share::schema;

class TestCoreTableProxy : public ::testing::Test {
public:
  virtual void SetUp();
  virtual void TearDown()
  {}

protected:
  int insert_data(const char* table, int64_t cnt);

  void verify_data(ObCoreTableProxy& kv);

protected:
  schema::DBInitializer initer_;
};

void TestCoreTableProxy::SetUp()
{
  int ret = initer_.init();

  ASSERT_EQ(OB_SUCCESS, ret);

  ret = initer_.create_system_table(true);
  ASSERT_EQ(OB_SUCCESS, ret);

  ret = insert_data("abc", 5);
  ASSERT_EQ(OB_SUCCESS, ret);
}

int TestCoreTableProxy::insert_data(const char* table, int64_t cnt)
{
  ObCoreTableProxy kv(table, initer_.get_sql_proxy());
  ObArray<ObCoreTableProxy::UpdateCell> cells;
  int ret = kv.load_for_update();
  if (OB_FAIL(ret)) {
    return ret;
  }

  for (int64_t i = 0; OB_SUCC(ret) && i < cnt; i++) {
    ObDMLSqlSplicer dml(ObDMLSqlSplicer::NAKED_VALUE_MODE);
    ObArray<ObCoreTableProxy::UpdateCell> cells;
    if (OB_FAIL(dml.add_pk_column("k1", i)) || OB_FAIL(dml.add_pk_column("k2", "aa")) ||
        OB_FAIL(dml.add_column("c1", (i + 1) * 100)) || OB_FAIL(dml.add_column("c2", "zj")) ||
        OB_FAIL(dml.splice_core_cells(kv, cells))) {}
    int64_t affected_rows = 0;
    if (OB_FAIL(ret)) {
      LOG_WARN("splice core kv cells failed", K(ret));
    } else if (OB_FAIL(kv.replace_row(cells, affected_rows))) {
      LOG_WARN("replace row failed", K(ret));
    }
  }
  return ret;
}

void TestCoreTableProxy::verify_data(ObCoreTableProxy& kv)
{
  ObCoreTableProxy kv2("abc", initer_.get_sql_proxy());
  int ret = kv2.load();
  ASSERT_EQ(OB_SUCCESS, ret);

  ASSERT_EQ(kv.row_count(), kv2.row_count());
  for (int64_t i = 0; i < kv.row_count(); ++i) {
    const ObCoreTableProxy::Row& lr = kv.get_all_row().at(i);
    const ObCoreTableProxy::Row* rr = NULL;
    FOREACH_CNT_X(row, kv2.get_all_row(), NULL == rr)
    {
      if (lr.get_row_id() == row->get_row_id()) {
        rr = &(*row);
      }
    }
    ASSERT_TRUE(NULL != rr);

    ASSERT_EQ(lr.get_row_id(), rr->get_row_id());
    const char* cols[] = {"k1", "k2", "c1", "c2"};
    for (int64_t k = 0; k < ARRAYSIZEOF(cols); ++k) {
      const char* c = cols[k];
      ObCoreTableProxy::Cell* l = NULL;
      ret = lr.get_cell(c, l);
      if (OB_ENTRY_NOT_EXIST == ret) {
        l = NULL;
      }
      ObCoreTableProxy::Cell* r = NULL;
      ret = rr->get_cell(c, r);
      if (OB_ENTRY_NOT_EXIST == ret) {
        r = NULL;
      }
      ASSERT_TRUE(NULL != l);
      ASSERT_TRUE(NULL != r);

      ASSERT_EQ(l->name_, r->name_);
      ASSERT_EQ(l->value_, r->value_);
    }
  }
}

TEST_F(TestCoreTableProxy, load)
{
  ObCoreTableProxy kv("abc", initer_.get_sql_proxy());

  int ret = kv.load();
  ASSERT_EQ(OB_SUCCESS, ret);

  LOG_INFO("all rows", K(kv.get_all_row()));

  ASSERT_EQ(5, kv.row_count());
  for (int64_t i = 0; i < 5; ++i) {
    ret = kv.next();
    ASSERT_EQ(OB_SUCCESS, ret);
    const ObCoreTableProxy::Row* row = NULL;
    ASSERT_EQ(OB_SUCCESS, kv.get_cur_row(row));
    ASSERT_EQ(i, row->get_row_id());
  }
  ret = kv.next();
  ASSERT_EQ(OB_ITER_END, ret);

  kv.seek_to_head();
  ret = kv.next();
  ASSERT_EQ(OB_SUCCESS, ret);

  int64_t int_val = -1;
  ret = kv.get_int("k1", int_val);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(0, int_val);

  ret = kv.get_int("k_not_exist", int_val);
  ASSERT_EQ(OB_ERR_NULL_VALUE, ret);

  ObString str;
  ret = kv.get_varchar("c2", str);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(0, str.compare("zj"));
}

TEST_F(TestCoreTableProxy, null_value)
{
  ObCoreTableProxy kv("abc", initer_.get_sql_proxy());
  int ret = kv.load_for_update();
  ASSERT_EQ(OB_SUCCESS, ret);

  ObDMLSqlSplicer dml(ObDMLSqlSplicer::NAKED_VALUE_MODE);
  ObArray<ObCoreTableProxy::UpdateCell> cells;

  // update null value
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k1", 1));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c2", dml.NULL_VALUE));
  ASSERT_EQ(OB_SUCCESS, dml.splice_core_cells(kv, cells));
  int64_t affected_rows = 0;
  ret = kv.update_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(1, affected_rows);

  ret = kv.load_for_update();
  ASSERT_EQ(OB_SUCCESS, ret);

  ObString str;
  ret = kv.get_all_row().at(1).get_varchar("c2", str);
  ASSERT_EQ(OB_ERR_NULL_VALUE, ret);

  // insert null value
  cells.reuse();
  dml.reset();
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k1", 0));
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k2", "lalala"));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c1", dml.NULL_VALUE));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c2", dml.NULL_VALUE));
  ASSERT_EQ(OB_SUCCESS, dml.splice_core_cells(kv, cells));
  ret = kv.replace_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);

  ret = kv.load_for_update();
  ASSERT_EQ(OB_SUCCESS, ret);
  ret = kv.get_all_row().at(5).get_varchar("c1", str);
  ASSERT_EQ(OB_ERR_NULL_VALUE, ret);
  ret = kv.get_all_row().at(5).get_varchar("c2", str);
  ASSERT_EQ(OB_ERR_NULL_VALUE, ret);
}

TEST_F(TestCoreTableProxy, update_or_insert)
{
  ObCoreTableProxy kv("abc", initer_.get_sql_proxy());
  int ret = kv.load();
  ASSERT_EQ(OB_SUCCESS, ret);

  ObDMLSqlSplicer dml(ObDMLSqlSplicer::NAKED_VALUE_MODE);
  ObArray<ObCoreTableProxy::UpdateCell> cells;

  // update one row
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k1", 1));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c2", "hz"));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("non_exist_column", 1024));
  ASSERT_EQ(OB_SUCCESS, dml.splice_core_cells(kv, cells));

  int64_t affected_rows = 0;
  ret = kv.update_row(cells, affected_rows);
  ASSERT_NE(OB_SUCCESS, ret);

  ret = kv.load_for_update();
  ASSERT_EQ(OB_SUCCESS, ret);

  ret = kv.update_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(1, affected_rows);
  ObString str;
  ret = kv.get_all_row().at(1).get_varchar("c2", str);
  ASSERT_EQ(0, str.compare("hz"));

  // update all rows
  dml.reset();
  ASSERT_EQ(OB_SUCCESS, dml.add_column("k2", 88));
  ASSERT_EQ(OB_SUCCESS, dml.splice_core_cells(kv, cells));
  ret = kv.update_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(5, affected_rows);
  FOREACH_CNT(r, kv.get_all_row())
  {
    int64_t int_val = 0;
    ret = r->get_int("k2", int_val);
    ASSERT_EQ(OB_SUCCESS, ret);
    ASSERT_EQ(88, int_val);
  }

  // update empty rows
  dml.reset();
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k1", 0));
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k2", "lalala"));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c1", -1));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c2", "xxx"));
  ASSERT_EQ(OB_SUCCESS, dml.splice_core_cells(kv, cells));
  ret = kv.update_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(0, affected_rows);

  // inset new row
  ret = kv.replace_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(1, affected_rows);
  ASSERT_EQ(6, kv.row_count());
  ASSERT_EQ(5, kv.get_all_row().at(5).get_row_id());

  // after all the operations, verify db record with memory rows
  verify_data(kv);
}

TEST_F(TestCoreTableProxy, delete_row)
{
  ObCoreTableProxy kv("abc", initer_.get_sql_proxy());
  int ret = kv.load_for_update();
  ASSERT_EQ(OB_SUCCESS, ret);

  ObDMLSqlSplicer dml(ObDMLSqlSplicer::NAKED_VALUE_MODE);
  ObArray<ObCoreTableProxy::UpdateCell> cells;

  // delete all
  int64_t affected_rows = 0;
  ret = kv.delete_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(5, affected_rows);

  ret = kv.load_for_update();
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(0, kv.row_count());

  ret = insert_data("abc", 5);
  ASSERT_EQ(OB_SUCCESS, ret);

  ret = kv.load_for_update();
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(5, kv.row_count());

  // delete one row and insert
  dml.reset();
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k1", 1));
  ASSERT_EQ(OB_SUCCESS, dml.splice_core_cells(kv, cells));
  ASSERT_EQ(OB_SUCCESS, ret);

  ret = kv.delete_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(1, affected_rows);

  ASSERT_EQ(4, kv.row_count());
  ASSERT_EQ(0, kv.get_all_row().at(0).get_row_id());
  ASSERT_EQ(2, kv.get_all_row().at(1).get_row_id());
  ASSERT_EQ(3, kv.get_all_row().at(2).get_row_id());
  ASSERT_EQ(4, kv.get_all_row().at(3).get_row_id());

  dml.reset();
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k1", 0));
  ASSERT_EQ(OB_SUCCESS, dml.add_pk_column("k2", "lalala"));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c1", -1));
  ASSERT_EQ(OB_SUCCESS, dml.add_column("c2", "xxx"));
  ASSERT_EQ(OB_SUCCESS, dml.splice_core_cells(kv, cells));
  ret = kv.replace_row(cells, affected_rows);
  ASSERT_EQ(OB_SUCCESS, ret);
  ASSERT_EQ(1, affected_rows);
  ASSERT_EQ(5, kv.row_count());
  ASSERT_EQ(1, kv.get_all_row().at(4).get_row_id());

  verify_data(kv);
}

}  // end namespace share
}  // end namespace oceanbase

int main(int argc, char** argv)
{
  oceanbase::common::ObLogger::get_logger().set_log_level("INFO");
  OB_LOGGER.set_log_level("INFO");
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
