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

#include "observer/virtual_table/ob_all_plan_cache_stat.h"

#include "common/object/ob_object.h"

#include "sql/plan_cache/ob_plan_cache_manager.h"
#include "sql/plan_cache/ob_plan_cache.h"
#include "sql/plan_cache/ob_plan_cache_callback.h"
#include "sql/plan_cache/ob_plan_cache_value.h"
#include "sql/plan_cache/ob_plan_cache_util.h"

#include "observer/ob_server_utils.h"
#include "observer/ob_server_struct.h"

using namespace oceanbase::common;
using namespace oceanbase::sql;
namespace oceanbase {
namespace observer {
ObAllPlanCacheBase::ObAllPlanCacheBase()
    : ObVirtualTableIterator(), pcm_(NULL), tenant_id_array_(), tenant_id_array_idx_(0)
{}

ObAllPlanCacheBase::~ObAllPlanCacheBase()
{
  reset();
}

void ObAllPlanCacheBase::reset()
{
  pcm_ = NULL;
  tenant_id_array_.reset();
  tenant_id_array_idx_ = 0;
}

int ObAllPlanCacheBase::inner_get_next_row(common::ObNewRow*& row)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(inner_get_next_row())) {
    if (OB_ITER_END != ret) {
      SERVER_LOG(WARN, "fail to get next row", K(ret));
    }
  } else {
    row = &cur_row_;
  }
  return ret;
}

int ObAllPlanCacheStat::fill_cells(ObPlanCache& plan_cache)
{
#define SET_REF_HANDLE_COL(handle)                      \
  int64_t ref_idx = handle;                             \
  int64_t ref_cnt = ref_handle_mgr.get_ref_cnt(handle); \
  cells[i].set_int(ref_cnt);

  int ret = OB_SUCCESS;
  const int64_t col_count = output_column_ids_.count();
  ObObj* cells = cur_row_.cells_;
  const ObPlanCacheStat& pc_stat = plan_cache.get_plan_cache_stat();
  ObString ipstr;
  const ObCacheRefHandleMgr& ref_handle_mgr = plan_cache.get_ref_handle_mgr();
  for (int64_t i = 0; OB_SUCC(ret) && i < col_count; ++i) {
    uint64_t col_id = output_column_ids_.at(i);
    switch (col_id) {
        // tenant id
      case TENANT_ID: {
        cells[i].set_int(plan_cache.get_tenant_id());
        break;
      }
        // svr_ip
      case SVR_IP: {
        ipstr.reset();
        if (OB_FAIL(ObServerUtils::get_server_ip(allocator_, ipstr))) {
          SERVER_LOG(ERROR, "get server ip failed", K(ret));
        } else {
          cells[i].set_varchar(ipstr);
          cells[i].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
        }
        break;
      }
        // svr_port
      case SVR_PORT: {
        cells[i].set_int(GCTX.self_addr_.get_port());
        break;
      }
        // sql_num
      case SQL_NUM: {
        cells[i].set_int(plan_cache.count());
        break;
      }
        // mem_used
      case MEM_USED: {
        cells[i].set_int(plan_cache.get_mem_used());
        break;
      }
      case MEM_HOLD: {
        cells[i].set_int(plan_cache.get_mem_hold());
        break;
      }
      case ACCESS_COUNT: {
        cells[i].set_int(pc_stat.access_count_);
        break;
      }
      case HIT_COUNT: {
        cells[i].set_int(pc_stat.hit_count_);
        break;
      }
      // hit_rate
      case HIT_RATE: {
        if (pc_stat.access_count_ != 0) {
          cells[i].set_int(pc_stat.hit_count_ * 100 / pc_stat.access_count_);
          SERVER_LOG(DEBUG, "rate:", "hit_count", pc_stat.hit_count_, "access_count", pc_stat.access_count_);
        } else {
          cells[i].set_int(0);
        }
        break;
      }
      // plan_num
      case PLAN_NUM: {  // id->plan_stat map size;
        cells[i].set_int(plan_cache.get_plan_num());
        break;
      }
        // mem_limit
      case MEM_LIMIT: {
        cells[i].set_int(plan_cache.get_mem_limit());
        break;
      }
        // hash_bucket
      case HASH_BUCKET: {
        cells[i].set_int(plan_cache.get_bucket_num());
        break;
      }
        // stmtkey_num, not used
      case STMTKEY_NUM: {
        cells[i].set_int(0);
        break;
      }
      case PC_REF_PLAN_LOCAL: {
        SET_REF_HANDLE_COL(PC_REF_PLAN_LOCAL_HANDLE);
        break;
      }
      case PC_REF_PLAN_REMOTE: {
        SET_REF_HANDLE_COL(PC_REF_PLAN_REMOTE_HANDLE);
        break;
      }
      case PC_REF_PLAN_DIST: {
        SET_REF_HANDLE_COL(PC_REF_PLAN_DIST_HANDLE);
        break;
      }
      case PC_REF_PLAN_ARR: {
        SET_REF_HANDLE_COL(PC_REF_PLAN_ARR_HANDLE);
        break;
      }
      case PC_REF_PLAN_STAT: {
        SET_REF_HANDLE_COL(PC_REF_PLAN_STAT_HANDLE);
        break;
      }
      case PC_REF_PL: {
        SET_REF_HANDLE_COL(PC_REF_PL_HANDLE);
        break;
      }
      case PC_REF_PL_STAT: {
        SET_REF_HANDLE_COL(PC_REF_PL_STAT_HANDLE);
        break;
      }
      case PLAN_GEN: {
        SET_REF_HANDLE_COL(PLAN_GEN_HANDLE);
        break;
      }
      case CLI_QUERY: {
        SET_REF_HANDLE_COL(CLI_QUERY_HANDLE);
        break;
      }
      case OUTLINE_EXEC: {
        SET_REF_HANDLE_COL(OUTLINE_EXEC_HANDLE);
        break;
      }
      case PLAN_EXPLAIN: {
        SET_REF_HANDLE_COL(PLAN_EXPLAIN_HANDLE);
        break;
      }
      case ASYN_BASELINE: {
        SET_REF_HANDLE_COL(ASYN_BASELINE_HANDLE);
        break;
      }
      case LOAD_BASELINE: {
        SET_REF_HANDLE_COL(LOAD_BASELINE_HANDLE);
        break;
      }
      case PS_EXEC: {
        SET_REF_HANDLE_COL(PS_EXEC_HANDLE);
        break;
      }
      case GV_SQL: {
        SET_REF_HANDLE_COL(GV_SQL_HANDLE);
        break;
      }
      case PL_ANON: {
        SET_REF_HANDLE_COL(PL_ANON_HANDLE);
        break;
      }
      case PL_ROUTINE: {
        SET_REF_HANDLE_COL(PL_ROUTINE_HANDLE);
        break;
      }
      case PACKAGE_VAR: {
        SET_REF_HANDLE_COL(PACKAGE_VAR_HANDLE);
        break;
      }
      case PACKAGE_TYPE: {
        SET_REF_HANDLE_COL(PACKAGE_TYPE_HANDLE);
        break;
      }
      case PACKAGE_SPEC: {
        SET_REF_HANDLE_COL(PACKAGE_SPEC_HANDLE);
        break;
      }
      case PACKAGE_BODY: {
        SET_REF_HANDLE_COL(PACKAGE_BODY_HANDLE);
        break;
      }
      case PACKAGE_RESV: {
        SET_REF_HANDLE_COL(PACKAGE_RESV_HANDLE);
        break;
      }
      case GET_PKG: {
        SET_REF_HANDLE_COL(GET_PKG_HANDLE);
        break;
      }
      case INDEX_BUILDER: {
        SET_REF_HANDLE_COL(INDEX_BUILDER_HANDLE);
        break;
      }
      case PCV_SET: {
        SET_REF_HANDLE_COL(PCV_SET_HANDLE);
        break;
      }
      case PCV_RD: {
        SET_REF_HANDLE_COL(PCV_RD_HANDLE);
        break;
      }
      case PCV_WR: {
        SET_REF_HANDLE_COL(PCV_WR_HANDLE);
        break;
      }
      case PCV_GET_PLAN_KEY: {
        SET_REF_HANDLE_COL(PCV_GET_PLAN_KEY_HANDLE);
        break;
      }
      case PCV_GET_PL_KEY: {
        SET_REF_HANDLE_COL(PCV_GET_PL_KEY_HANDLE);
        break;
      }
      case PCV_EXPIRE_BY_USED: {
        SET_REF_HANDLE_COL(PCV_EXPIRE_BY_USED_HANDLE);
        break;
      }
      case PCV_EXPIRE_BY_MEM: {
        SET_REF_HANDLE_COL(PCV_EXPIRE_BY_MEM_HANDLE);
        break;
      }
      default: {
        ret = OB_ERR_UNEXPECTED;
        SERVER_LOG(WARN, "invalid column id", K(ret), K(i), K(output_column_ids_), K(col_id));
        break;
      }
    }
  }
  return ret;
#undef SET_REF_HANDLE_COL
}

int ObAllPlanCacheStatI1::get_all_tenant_ids(ObIArray<uint64_t>& tenant_ids)
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(set_tenant_ids(key_ranges_))) {
    LOG_WARN("set tenant ids failed", K(ret));
  }

  for (int64_t i = 0; OB_SUCC(ret) && i < tenant_ids_.count(); i++) {
    // to keep the save interface
    ret = tenant_ids.push_back(tenant_ids_.at(i));
  }
  return ret;
}

int ObAllPlanCacheStat::get_all_tenant_ids(ObIArray<uint64_t>& tenant_ids)
{
  int ret = OB_SUCCESS;
  ObPlanCacheManager::ObGetAllCacheKeyOp op(&tenant_ids);
  if (OB_ISNULL(pcm_)) {
    ret = OB_INVALID_ARGUMENT;
    SERVER_LOG(WARN, "invalid args", KP(pcm_));
  } else if (OB_FAIL(pcm_->get_plan_cache_map().foreach_refactored(op))) {
    SERVER_LOG(WARN, "fail to travese map", K(ret));
  }
  return ret;
}

int ObAllPlanCacheStat::inner_open()
{
  int ret = OB_SUCCESS;
  if (OB_FAIL(get_all_tenant_ids(tenant_id_array_))) {
    SERVER_LOG(WARN, "fail get all tenant ids", K(ret));
  }
  return ret;
}
int ObAllPlanCacheStat::get_row_from_tenants()
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(NULL == pcm_)) {
    ret = OB_NOT_INIT;
    SERVER_LOG(WARN, "pcm_ is NULL", K(ret));
  } else {
    if (tenant_id_array_idx_ < 0) {
      ret = OB_ERR_UNEXPECTED;
      SERVER_LOG(WARN, "invalid tenant_id_array index", K(ret), K(tenant_id_array_idx_));
    } else if (tenant_id_array_idx_ >= tenant_id_array_.count()) {
      ret = OB_ITER_END;
    } else {
      uint64_t tenant_id = tenant_id_array_.at(tenant_id_array_idx_);
      ObPlanCache* plan_cache = pcm_->get_plan_cache(tenant_id);
      if (OB_UNLIKELY(NULL == plan_cache)) {
        SERVER_LOG(WARN, "invalid plan cache", K(ret), K(tenant_id));
      } else if (!plan_cache->is_valid()) {
        // do nothing
      } else {
        if (OB_FAIL(fill_cells(*plan_cache))) {
          SERVER_LOG(WARN, "fail to fill cells", K(ret), K(cur_row_));
        } else {
          SERVER_LOG(DEBUG, "add plan cache", K(tenant_id));
        }
        // release the plan cache
        plan_cache->dec_ref_count();
      }
      ++tenant_id_array_idx_;
    }
  }
  return ret;
}

int ObAllPlanCacheStatI1::set_tenant_ids(const common::ObIArray<common::ObNewRange>& ranges)
{
  int ret = OB_SUCCESS;
  ObRowkey start_key;
  ObRowkey end_key;
  for (int64_t i = 0; OB_SUCC(ret) && i < ranges.count(); ++i) {
    int64_t tenant_id = -1;
    start_key.reset();
    end_key.reset();
    start_key = ranges.at(i).start_key_;
    end_key = ranges.at(i).end_key_;
    if (!(start_key.get_obj_cnt() > 0)) {
      ret = OB_ERR_UNEXPECTED;
      SERVER_LOG(WARN, "assert start_key.get_obj_cnt() > 0", K(ret));
    } else if (!(start_key.get_obj_cnt() == end_key.get_obj_cnt())) {
      ret = OB_ERR_UNEXPECTED;
      SERVER_LOG(WARN, "assert start_key.get_obj_cnt() == end_key.get_obj_cnt()", K(ret));
    }
    const ObObj* start_key_obj_ptr = NULL;
    const ObObj* end_key_obj_ptr = NULL;
    if (OB_SUCC(ret)) {
      start_key_obj_ptr = start_key.get_obj_ptr();
      end_key_obj_ptr = end_key.get_obj_ptr();
      if (OB_ISNULL(start_key_obj_ptr) || OB_ISNULL(end_key_obj_ptr)) {
        ret = OB_INVALID_ARGUMENT;
        SERVER_LOG(WARN, "invalid args", KP(start_key_obj_ptr), KP(end_key_obj_ptr));
      } else if ((!start_key_obj_ptr[0].is_min_value() || !end_key_obj_ptr[0].is_max_value()) &&
                 start_key_obj_ptr[0] != end_key_obj_ptr[0]) {
        ret = OB_NOT_IMPLEMENT;
        SERVER_LOG(WARN, "tenant id exact value", K(ret));
      } else if (start_key_obj_ptr[0] == end_key_obj_ptr[0]) {
        if (!(ObIntType == start_key_obj_ptr[0].get_type())) {
          ret = OB_ERR_UNEXPECTED;
          SERVER_LOG(WARN, "assert ObIntType == start_key_obj_ptr[0].get_type()", K(ret));
        } else if (!(start_key_obj_ptr[0].get_type() == end_key_obj_ptr[0].get_type())) {
          ret = OB_ERR_UNEXPECTED;
          SERVER_LOG(WARN, "assert start_key_obj_ptr[0].get_type() == end_key_obj_ptr[0].get_type()", K(ret));
        } else {
          tenant_id = start_key_obj_ptr[0].get_int();
          if (!(tenant_id >= 0)) {
            ret = OB_ERR_UNEXPECTED;
            SERVER_LOG(WARN, "assert tenant_id >= 0", K(ret));
          } else if (OB_FAIL(add_var_to_array_no_dup(tenant_ids_, static_cast<uint64_t>(tenant_id)))) {
            SERVER_LOG(WARN, "Failed to add tenant_id to array no duplicate", K(ret));
          } else {
          }  // do nothing
        }
      }
    }
  }
  return ret;
}

}  // end of namespace observer
}  // end of namespace oceanbase
