/**
 * 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 "lib/container/ob_array_serialization.h"
#include "share/schema/ob_schema_getter_guard.h"
#include "share/schema/ob_schema_struct.h"
#include "share/schema/ob_table_schema.h"
#include "share/schema/ob_schema_utils.h"
#include "observer/virtual_table/ob_table_index.h"

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

ObTableIndex::ObTableIndex()
    : ObVirtualTableScannerIterator(),
      tenant_id_(OB_INVALID_ID),
      show_table_id_(OB_INVALID_ID),
      database_schemas_(),
      database_schema_idx_(OB_INVALID_ID),
      table_schemas_(),
      table_schema_idx_(OB_INVALID_ID),
      rowkey_info_idx_(OB_INVALID_ID),
      index_tid_array_idx_(OB_INVALID_ID),
      index_column_idx_(OB_INVALID_ID),
      simple_index_infos_(),
      is_rowkey_end_(false),
      is_normal_end_(false),
      ft_dep_col_idx_(OB_INVALID_ID)
{}

ObTableIndex::~ObTableIndex()
{}

int ObTableIndex::inner_open()
{
  int ret = OB_SUCCESS;
  for (int64_t i = 0; OB_SUCC(ret) && OB_INVALID_ID == show_table_id_ && i < key_ranges_.count(); ++i) {
    const ObRowkey& start_key = key_ranges_.at(i).start_key_;
    const ObRowkey& end_key = key_ranges_.at(i).end_key_;
    const ObObj* start_key_obj_ptr = start_key.get_obj_ptr();
    const ObObj* end_key_obj_ptr = end_key.get_obj_ptr();
    if (start_key.get_obj_cnt() > 0 && start_key.get_obj_cnt() == end_key.get_obj_cnt()) {
      if (OB_UNLIKELY(NULL == start_key_obj_ptr || NULL == end_key_obj_ptr)) {
        ret = OB_ERR_UNEXPECTED;
        SERVER_LOG(WARN, "key obj ptr is NULL", K(ret), K(start_key_obj_ptr), K(end_key_obj_ptr));
      } else if (start_key_obj_ptr[0] == end_key_obj_ptr[0] && ObIntType == start_key_obj_ptr[0].get_type()) {
        show_table_id_ = start_key_obj_ptr[0].get_int();
      } else { /*do nothing*/
      }
    }
  }
  return ret;
}
void ObTableIndex::reset()
{
  ObVirtualTableScannerIterator::reset();
  key_ranges_.reset();
  tenant_id_ = OB_INVALID_ID;
  show_table_id_ = OB_INVALID_ID;
  database_schemas_.reset();
  database_schema_idx_ = OB_INVALID_ID;
  table_schemas_.reset();
  table_schema_idx_ = OB_INVALID_ID;
  rowkey_info_idx_ = OB_INVALID_ID;
  index_tid_array_idx_ = OB_INVALID_ID;
  index_column_idx_ = OB_INVALID_ID;
  is_normal_end_ = false;
  is_rowkey_end_ = false;
  simple_index_infos_.reset();
  ft_dep_col_idx_ = OB_INVALID_ID;
}

int ObTableIndex::inner_get_next_row(common::ObNewRow*& row)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(allocator_) || OB_ISNULL(schema_guard_) || OB_ISNULL(cur_row_.cells_)) {
    ret = OB_NOT_INIT;
    SERVER_LOG(WARN, "data member is not init", K(ret), K(allocator_), K(schema_guard_), K(cur_row_.cells_));
  } else {
    ObObj* cells = cur_row_.cells_;
    const int64_t col_count = output_column_ids_.count();
    bool is_end = false;
    if (OB_INVALID_ID == show_table_id_) {
      if (tenant_id_ == OB_INVALID_ID) {
        ret = OB_ITER_END;
      } else {
        if (OB_INVALID_ID == static_cast<uint64_t>(database_schema_idx_)) {  // first get next row
          if (OB_FAIL(schema_guard_->get_database_schemas_in_tenant(tenant_id_, database_schemas_))) {
            SERVER_LOG(WARN, "failed to get database schema of tenant", K_(tenant_id));
          } else {
            database_schema_idx_ = 0;
          }
        }
        if (OB_SUCC(ret)) {
          do {
            is_end = false;
            if (OB_UNLIKELY(database_schema_idx_ < 0)) {
              ret = OB_ERR_UNEXPECTED;
              SERVER_LOG(WARN, "database_schema_idx_ is invalid", K(ret), K(database_schema_idx_));
            } else if (database_schema_idx_ >= database_schemas_.count()) {
              ret = OB_ITER_END;
              database_schema_idx_ = OB_INVALID_ID;
            } else {
              const ObDatabaseSchema* database_schema = database_schemas_.at(database_schema_idx_);
              if (OB_UNLIKELY(NULL == database_schema)) {
                ret = OB_ERR_BAD_DATABASE;
                SERVER_LOG(WARN, "database not exist", K(ret));
              } else if (database_schema->is_in_recyclebin()) {
                ++database_schema_idx_;
                is_end = true;  // ignore this database
              } else if (ObString(OB_RECYCLEBIN_SCHEMA_NAME) == database_schema->get_database_name_str() ||
                         ObSchemaUtils::is_public_database(
                             database_schema->get_database_name_str(), lib::is_oracle_mode())) {
                ++database_schema_idx_;
                is_end = true;
              } else if (OB_FAIL(add_database_indexes(*database_schema, cells, col_count, is_end))) {
                SERVER_LOG(WARN, "failed to add table constraint of database schema!", K(ret));
              } else {
                if (is_end) {
                  ++database_schema_idx_;
                }
              }
            }
          } while (OB_SUCC(ret) && is_end);
        }
      }
    } else {
      const ObTableSchema* table_schema = NULL;
      const ObDatabaseSchema* database_schema = NULL;
      if (OB_FAIL(schema_guard_->get_table_schema(show_table_id_, table_schema))) {
        SERVER_LOG(WARN, "fail to get table schema", K(ret));
      } else if (OB_UNLIKELY(NULL == table_schema)) {
        ret = OB_TABLE_NOT_EXIST;
        SERVER_LOG(WARN, "fail to get table schema", K(ret), K(show_table_id_));
      } else if (OB_FAIL(schema_guard_->get_database_schema(table_schema->get_database_id(), database_schema))) {
        SERVER_LOG(WARN, "fail to get database schema", K(ret), "database_id", table_schema->get_database_id());
      } else if (OB_UNLIKELY(NULL == database_schema)) {
        ret = OB_ERR_BAD_DATABASE;
        SERVER_LOG(WARN, "fail to get database schema", K(ret), "database_id", table_schema->get_database_id());
      } else if (OB_FAIL(add_table_indexes(
                     *table_schema, database_schema->get_database_name_str(), cells, col_count, is_end))) {
        SERVER_LOG(WARN, "failed to add table indexes of table schema", "table_schema", *table_schema, K(ret));
      } else { /*do nothing*/
      }
      if (is_end) {
        ret = OB_ITER_END;
      }
    }
    if (OB_SUCC(ret)) {
      row = &cur_row_;
    }
  }
  return ret;
}

int ObTableIndex::add_database_indexes(
    const ObDatabaseSchema& database_schema, ObObj* cells, int64_t col_count, bool& is_end)
{
  int ret = OB_SUCCESS;
  bool is_sub_end = false;
  if (OB_INVALID_ID == static_cast<uint64_t>(table_schema_idx_)) {
    if (OB_ISNULL(schema_guard_)) {
      ret = OB_ERR_UNEXPECTED;
      SERVER_LOG(WARN, "data member is not init", K(ret), K(schema_guard_));
    } else if (OB_FAIL(schema_guard_->get_table_schemas_in_database(
                   tenant_id_, database_schema.get_database_id(), table_schemas_))) {
      SERVER_LOG(WARN, "failed to get table schema in database", K(ret));
    } else {
      table_schema_idx_ = 0;
    }
  }
  if (OB_SUCC(ret)) {
    do {
      is_sub_end = false;
      if (OB_UNLIKELY(table_schema_idx_ < 0)) {
        ret = OB_ERR_UNEXPECTED;
        SERVER_LOG(WARN, "table_schema_idx_ is wrong", K(table_schema_idx_));
      } else if (table_schema_idx_ >= table_schemas_.count()) {
        is_end = true;
        table_schema_idx_ = OB_INVALID_ID;
        table_schemas_.reset();
      } else {
        is_end = false;
        const ObTableSchema* table_schema = table_schemas_.at(table_schema_idx_);
        if (OB_UNLIKELY(NULL == table_schema)) {
          ret = OB_TABLE_NOT_EXIST;
          SERVER_LOG(WARN, "table schema not exist", K(ret));
        } else if (table_schema->is_index_table()) {
          is_sub_end = true;
        } else if (OB_FAIL(add_table_indexes(
                       *table_schema, database_schema.get_database_name_str(), cells, col_count, is_sub_end))) {
          SERVER_LOG(WARN, "failed to add table constraint of table schema", "table_schema", *table_schema, K(ret));
        } else { /*do nothing*/
        }
        if (OB_LIKELY(OB_SUCC(ret) && is_sub_end)) {
          ++table_schema_idx_;
          is_rowkey_end_ = false;
          is_normal_end_ = false;
        }
      }
    } while (OB_SUCC(ret) && is_sub_end);
  }
  return ret;
}

int ObTableIndex::add_table_indexes(
    const ObTableSchema& table_schema, const ObString& database_name, ObObj* cells, int64_t col_count, bool& is_end)
{

  int ret = OB_SUCCESS;
  if (!is_rowkey_end_) {
    // add rowkey constraints
    if (OB_FAIL(add_rowkey_indexes(table_schema, database_name, cells, col_count, is_rowkey_end_))) {
      SERVER_LOG(WARN, "fail to add rowkey indexes", K(ret));
    }
    if (OB_SUCC(ret) && is_rowkey_end_) {
      if (OB_FAIL(add_normal_indexes(table_schema, database_name, cells, col_count, is_normal_end_))) {
        SERVER_LOG(WARN, "fail to add normal indexes", K(ret));
      }
    }
  } else {
    if (OB_FAIL(add_normal_indexes(table_schema, database_name, cells, col_count, is_normal_end_))) {
      SERVER_LOG(WARN, "fail to add normal indexes", K(ret));
    }
  }
  is_end = is_rowkey_end_ && is_normal_end_;
  return ret;
}

int ObTableIndex::add_rowkey_indexes(
    const ObTableSchema& table_schema, const ObString& database_name, ObObj* cells, int64_t col_count, bool& is_end)
{
  int ret = OB_SUCCESS;
  const ObRowkeyInfo& rowkey_info = table_schema.get_rowkey_info();
  if (OB_INVALID_ID == static_cast<uint64_t>(rowkey_info_idx_)) {
    rowkey_info_idx_ = 0;
  }
  if (OB_ISNULL(cells)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "parameter is NULL", K(ret), K(cells));
  } else if (OB_UNLIKELY(cur_row_.count_ < col_count)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "cells count is less than output column count", K(ret), K(cur_row_.count_), K(col_count));
  } else if (OB_UNLIKELY(rowkey_info_idx_ < 0)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "rowkey_info_idx_ is wrong", K(rowkey_info_idx_));
  } else if (rowkey_info_idx_ >= rowkey_info.get_size()) {
    is_end = true;
    rowkey_info_idx_ = OB_INVALID_ID;
  } else {
    const ObColumnSchemaV2* column_schema = NULL;
    ObRowkeyColumn rowkey_col;
    if (table_schema.is_no_pk_table()) {
      // don't show hidden pk
      // used for only hidden pk in the RowKey Table_schema
      is_end = true;
      rowkey_info_idx_ = OB_INVALID_ID;
    } else if (OB_FAIL(rowkey_info.get_column(rowkey_info_idx_, rowkey_col))) {
      SERVER_LOG(WARN, "fail to get column", K(ret));
    } else if (OB_UNLIKELY(NULL == (column_schema = table_schema.get_column_schema(rowkey_col.column_id_)))) {
      ret = OB_ERR_UNEXPECTED;
      SERVER_LOG(WARN, "fail to get column schema", K(ret), K(rowkey_col.column_id_));
    } else {
      is_end = false;
      uint64_t cell_idx = 0;
      for (int64_t j = 0; OB_SUCC(ret) && j < col_count; ++j) {
        uint64_t col_id = output_column_ids_.at(j);
        switch (col_id) {
          // table_id
          case OB_APP_MIN_COLUMN_ID: {
            cells[cell_idx].set_int(table_schema.get_table_id());
            break;
          }
          // key_name
          case OB_APP_MIN_COLUMN_ID + 1: {
            cells[cell_idx].set_varchar(ObString("PRIMARY"));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // seq_in_index
          case OB_APP_MIN_COLUMN_ID + 2: {
            cells[cell_idx].set_int(rowkey_info_idx_ + 1);
            break;
          }
          // table_schema
          case OB_APP_MIN_COLUMN_ID + 3: {
            cells[cell_idx].set_varchar(database_name);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // table
          case OB_APP_MIN_COLUMN_ID + 4: {
            cells[cell_idx].set_varchar(table_schema.get_table_name_str());
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // non_unique
          case OB_APP_MIN_COLUMN_ID + 5: {
            cells[cell_idx].set_int(0);
            break;
          }
          // index_schema
          case OB_APP_MIN_COLUMN_ID + 6: {
            cells[cell_idx].set_varchar(database_name);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // column_name
          case OB_APP_MIN_COLUMN_ID + 7: {
            cells[cell_idx].set_varchar(column_schema->get_column_name_str());
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // collation
          case OB_APP_MIN_COLUMN_ID + 8: {
            cells[cell_idx].set_varchar(ObString("A"));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // cardinality
          case OB_APP_MIN_COLUMN_ID + 9: {
            // TODO: not implemented yet
            cells[cell_idx].set_null();
            break;
          }
          // sub_part
          case OB_APP_MIN_COLUMN_ID + 10: {
            // TODO: not implemented yet
            cells[cell_idx].set_null();
            break;
          }
          // packed
          case OB_APP_MIN_COLUMN_ID + 11: {
            // TODO: not implemented yet
            cells[cell_idx].set_null();
            break;
          }
          // null
          case OB_APP_MIN_COLUMN_ID + 12: {
            cells[cell_idx].set_varchar(ObString(""));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // index_type
          case OB_APP_MIN_COLUMN_ID + 13: {
            cells[cell_idx].set_varchar(ObString("BTREE"));  // FIXME only BTREE?
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // comment
          case OB_APP_MIN_COLUMN_ID + 14: {
            // TODO
            cells[cell_idx].set_varchar(ObString("available"));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          // index_comment
          case OB_APP_MIN_COLUMN_ID + 15: {
            // TODO
            cells[cell_idx].set_varchar(ObString(""));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // is_visible
          case OB_APP_MIN_COLUMN_ID + 16: {
            cells[cell_idx].set_varchar(ObString("YES"));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }

          default: {
            ret = OB_ERR_UNEXPECTED;
            SERVER_LOG(WARN,
                "invalid column id",
                K(ret),
                K(cell_idx),
                K(rowkey_info_idx_),
                K(j),
                K(output_column_ids_),
                K(col_id));
            break;
          }
        }
        if (OB_SUCC(ret)) {
          cell_idx++;
        }
      }
      ++rowkey_info_idx_;
    }
  }
  return ret;
}

int ObTableIndex::add_normal_indexes(
    const ObTableSchema& table_schema, const ObString& database_name, ObObj* cells, int64_t col_count, bool& is_end)
{
  int ret = OB_SUCCESS;
  bool is_sub_end = false;
  if (OB_INVALID_ID == static_cast<uint64_t>(index_tid_array_idx_)) {
    simple_index_infos_.reset();
    if (OB_FAIL(table_schema.get_simple_index_infos_without_delay_deleted_tid(simple_index_infos_, false))) {
      SERVER_LOG(WARN, "cannot get index list", K(ret));
    } else {
      index_tid_array_idx_ = 0;
    }
  }

  if (OB_SUCC(ret)) {
    do {
      is_sub_end = false;
      if (OB_UNLIKELY(index_tid_array_idx_ < 0)) {
        ret = OB_ERR_UNEXPECTED;
        SERVER_LOG(WARN, "index_tid_array_idx_ is wrong", K(index_tid_array_idx_));
      } else if (index_tid_array_idx_ >= simple_index_infos_.count()) {
        is_end = true;
        index_tid_array_idx_ = OB_INVALID_ID;
      } else {
        is_end = false;
        const ObTableSchema* index_schema = NULL;
        if (OB_ISNULL(schema_guard_)) {
          ret = OB_NOT_INIT;
          SERVER_LOG(WARN, "schema guard is not init", K(ret), K(schema_guard_));
        } else if (OB_UNLIKELY(OB_FAIL(schema_guard_->get_table_schema(
                       simple_index_infos_.at(index_tid_array_idx_).table_id_, index_schema)))) {
          SERVER_LOG(WARN,
              "fail to get index table",
              K(ret),
              "index_table_id",
              simple_index_infos_.at(index_tid_array_idx_).table_id_);
        } else if (OB_UNLIKELY(NULL == index_schema)) {
          ret = OB_ERR_UNEXPECTED;
          SERVER_LOG(WARN,
              "invalid index table id",
              K(ret),
              "index_table_id",
              simple_index_infos_.at(index_tid_array_idx_).table_id_);
        } else {
          bool is_ctxcat_fulltext = false;
          ObArray<uint64_t> ft_gen_column_ids;
          ObArray<uint64_t> dep_column_ids;
          if ((INDEX_TYPE_DOMAIN_CTXCAT == index_schema->get_index_type())) {
            if (OB_FAIL(index_schema->get_generated_column_ids(ft_gen_column_ids))) {
              LOG_WARN("get generated column ids failed", K(ret));
            } else if (1 == ft_gen_column_ids.count()) {
              // for text indexed table, only one generated column is allowed,which can depend on
              //  multi columns
              is_ctxcat_fulltext = true;
            }
          }

          if (OB_FAIL(ret)) {
          } else if (is_ctxcat_fulltext) {
            const ObColumnSchemaV2* gen_column_schema = NULL;
            if (OB_INVALID_ID == static_cast<uint64_t>(ft_dep_col_idx_)) {
              ft_dep_col_idx_ = 0;
            }
            if (OB_ISNULL(gen_column_schema = table_schema.get_column_schema(ft_gen_column_ids[0]))) {
              ret = OB_SCHEMA_ERROR;
              SERVER_LOG(WARN, "fail to get data table column schema", K(ret));
            } else if (OB_FAIL(gen_column_schema->get_cascaded_column_ids(dep_column_ids))) {
              LOG_WARN("get cascaded column ids from column schema failed", K(ret), K(*gen_column_schema));
            } else if (dep_column_ids.count() <= ft_dep_col_idx_) {
              is_sub_end = true;
              ft_dep_col_idx_ = OB_INVALID_ID;
            } else if (OB_FAIL(add_fulltext_index_column(database_name,
                           table_schema,
                           index_schema,
                           cells,
                           col_count,
                           dep_column_ids[ft_dep_col_idx_]))) {
              ret = OB_ERR_UNEXPECTED;
              SERVER_LOG(WARN, "fail to addd normal index column", K(ret), K(col_count), K(ft_dep_col_idx_));
            }
          } else {
            if (OB_FAIL(
                    add_normal_index_column(database_name, table_schema, index_schema, cells, col_count, is_sub_end))) {
              ret = OB_ERR_UNEXPECTED;
              SERVER_LOG(WARN, "fail to addd normal index column", K(ret), K(col_count), K(is_sub_end));
            }
          }
          if (OB_SUCC(ret)) {
            if (is_sub_end) {
              ++index_tid_array_idx_;
            }
          }
        }
      }
    } while (OB_SUCC(ret) && is_sub_end);
  }
  return ret;
}

int ObTableIndex::add_normal_index_column(const ObString& database_name, const ObTableSchema& table_schema,
    const ObTableSchema* index_schema, ObObj* cells, int64_t col_count, bool& is_end)
{
  int ret = OB_SUCCESS;
  if (OB_INVALID_ID == static_cast<uint64_t>(index_column_idx_)) {
    index_column_idx_ = 0;
  }
  if (OB_ISNULL(cells) || OB_ISNULL(index_schema) || OB_ISNULL(allocator_)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "parameter or data member is NULL", K(ret), K(cells), K(index_schema), K(allocator_));
  } else if (OB_UNLIKELY(cur_row_.count_ < col_count)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "cells count is less than output column count", K(ret), K(cur_row_.count_), K(col_count));
  } else if (OB_UNLIKELY(index_column_idx_ < 0)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "index_column_idx_ is wrong", K(ret));
  } else if (index_column_idx_ >= index_schema->get_index_info().get_size()) {
    is_end = true;
    index_column_idx_ = OB_INVALID_ID;
  } else {
    const ObIndexInfo& index_info = index_schema->get_index_info();
    const ObRowkeyColumn* rowkey_column = index_info.get_column(index_column_idx_);
    const ObColumnSchemaV2* column_schema = NULL;
    const ObColumnSchemaV2* index_column = NULL;
    ObString index_name;
    char* buf = NULL;
    int64_t buf_len = number::ObNumber::MAX_PRINTABLE_SIZE;
    if (OB_UNLIKELY(NULL == rowkey_column)) {
      ret = OB_SCHEMA_ERROR;
      SERVER_LOG(WARN, "fail to get rowkey column", K(ret));
    } else if (OB_ISNULL(column_schema = table_schema.get_column_schema(rowkey_column->column_id_))) {
      ret = OB_SCHEMA_ERROR;
      SERVER_LOG(WARN, "fail to get data table column schema", K(ret), K_(rowkey_column->column_id));
    } else if (OB_ISNULL(index_column = index_schema->get_column_schema(rowkey_column->column_id_))) {
      ret = OB_SCHEMA_ERROR;
      SERVER_LOG(WARN, "get index column schema failed", K_(rowkey_column->column_id));
    } else if (OB_ISNULL(buf = static_cast<char*>(allocator_->alloc(buf_len)))) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_WARN("allocate memory for print buffer failed", K(ret), K(buf_len));
    } else {
      is_end = false;
      uint64_t cell_idx = 0;
      for (int64_t j = 0; OB_SUCC(ret) && j < col_count; ++j) {
        uint64_t col_id = output_column_ids_.at(j);
        switch (col_id) {
          // table_id
          case OB_APP_MIN_COLUMN_ID: {
            cells[cell_idx].set_int(table_schema.get_table_id());
            break;
          }
            // key_name
          case OB_APP_MIN_COLUMN_ID + 1: {
            index_name.reset();
            //  get the original short index name
            if (OB_FAIL(ObTableSchema::get_index_name(
                    *allocator_, table_schema.get_table_id(), index_schema->get_table_name_str(), index_name))) {
              SERVER_LOG(WARN, "error get index table name failed", K(ret));
            } else {
              cells[cell_idx].set_varchar(index_name);
              cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            }
            break;
          }
            // seq_in_index
          case OB_APP_MIN_COLUMN_ID + 2: {
            cells[cell_idx].set_int(index_column_idx_ + 1);
            break;
          }
            // table_schema
          case OB_APP_MIN_COLUMN_ID + 3: {
            cells[cell_idx].set_varchar(database_name);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // table
          case OB_APP_MIN_COLUMN_ID + 4: {
            cells[cell_idx].set_varchar(table_schema.get_table_name_str());
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // non_unique
          case OB_APP_MIN_COLUMN_ID + 5: {
            int64_t non_unique = 0;
            if (index_schema->is_unique_index()) {
              non_unique = 0;
            } else {
              non_unique = 1;
            }
            cells[cell_idx].set_int(non_unique);
            break;
          }
            // index_schema
          case OB_APP_MIN_COLUMN_ID + 6: {
            cells[cell_idx].set_varchar(database_name);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // column_name
          case OB_APP_MIN_COLUMN_ID + 7: {
            ObString column_name;
            if (OB_FAIL(get_show_column_name(table_schema, *column_schema, column_name))) {
              LOG_WARN("get show column name failed", K(ret), K(table_schema), KPC(column_schema));
            } else {
              cells[cell_idx].set_varchar(column_name);
              cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            }
            break;
          }
            // collation
          case OB_APP_MIN_COLUMN_ID + 8: {
            cells[cell_idx].set_varchar(ObString("A"));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // cardinality
          case OB_APP_MIN_COLUMN_ID + 9: {
            // TODO: not implemented yet
            cells[cell_idx].set_null();
            break;
          }
            // sub_part
          case OB_APP_MIN_COLUMN_ID + 10: {
            // TODO: if only part of the string is indexed, it should be the substring character
            // count, otherwise, it should be NULL
            cells[cell_idx].reset();  // clean up for previous row
            if (column_schema->is_prefix_column()) {
              // print the prefix index length
              int64_t pos = 0;
              if (OB_FAIL(databuff_printf(buf, buf_len, pos, "%d", column_schema->get_data_length()))) {
                LOG_WARN(
                    "print prefix column data length failed", K(ret), KPC(column_schema), K(buf), K(buf_len), K(pos));
              } else {
                cells[cell_idx].set_varchar(ObString(pos, buf));
                cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
              }
            }
            break;
          }
            // packed
          case OB_APP_MIN_COLUMN_ID + 11: {
            // TODO: not implemented yet
            cells[cell_idx].set_null();
            break;
          }
            // null
          case OB_APP_MIN_COLUMN_ID + 12: {
            if (column_schema->is_rowkey_column()) {
              cells[cell_idx].set_varchar(ObString(""));
            } else if (column_schema->is_nullable()) {
              cells[cell_idx].set_varchar(ObString("YES"));
            } else {
              cells[cell_idx].set_varchar(ObString(""));
            }
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // index_type
          case OB_APP_MIN_COLUMN_ID + 13: {
            if (false) {
              cells[cell_idx].set_varchar(ObString("FULLTEXT"));
            } else {
              cells[cell_idx].set_varchar(ObString("BTREE"));  // FIXME: other types?
            }
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // comment
          case OB_APP_MIN_COLUMN_ID + 14: {
            // TODO
            cells[cell_idx].set_varchar(ObString(ob_index_status_str(index_schema->get_index_status())));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // index_comment
          case OB_APP_MIN_COLUMN_ID + 15: {
            cells[cell_idx].set_varchar(index_schema->get_comment_str());
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          case OB_APP_MIN_COLUMN_ID + 16: {
            const ObString& is_visible = index_schema->is_index_visible() ? "YES" : "NO";
            cells[cell_idx].set_varchar(is_visible);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          default: {
            ret = OB_ERR_UNEXPECTED;
            SERVER_LOG(WARN, "invalid column id", K(ret), K(cell_idx), K(output_column_ids_), K(col_id));
            break;
          }
        }
        if (OB_SUCC(ret)) {
          ++cell_idx;
        }
      }
      ++index_column_idx_;
    }
  }
  return ret;
}

int ObTableIndex::add_fulltext_index_column(const ObString& database_name, const ObTableSchema& table_schema,
    const ObTableSchema* index_schema, ObObj* cells, int64_t col_count, const uint64_t column_id)
{
  int ret = OB_SUCCESS;
  if (OB_ISNULL(cells) || OB_ISNULL(index_schema) || OB_ISNULL(allocator_)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "parameter or data member is NULL", K(ret), K(cells), K(index_schema), K(allocator_));
  } else if (OB_UNLIKELY(cur_row_.count_ < col_count)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "cells count is less than output column count", K(ret), K(cur_row_.count_), K(col_count));
  } else if (OB_UNLIKELY(OB_INVALID_ID == ft_dep_col_idx_ || ft_dep_col_idx_ < 0)) {
    ret = OB_ERR_UNEXPECTED;
    SERVER_LOG(WARN, "ft_dep_col_idx_ is wrong", K(ret));
  } else {
    const ObColumnSchemaV2* column_schema = NULL;
    ObString index_name;
    char* buf = NULL;
    int64_t buf_len = number::ObNumber::MAX_PRINTABLE_SIZE;
    if (OB_ISNULL(column_schema = table_schema.get_column_schema(column_id))) {
      ret = OB_SCHEMA_ERROR;
      SERVER_LOG(WARN, "fail to get data table column schema", K(ret), K(column_id));
    } else if (OB_ISNULL(buf = static_cast<char*>(allocator_->alloc(buf_len)))) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      LOG_WARN("allocate memory for print buffer failed", K(ret), K(buf_len));
    } else {
      uint64_t cell_idx = 0;
      for (int64_t j = 0; OB_SUCC(ret) && j < col_count; ++j) {
        uint64_t col_id = output_column_ids_.at(j);
        switch (col_id) {
          // table_id
          case OB_APP_MIN_COLUMN_ID: {
            cells[cell_idx].set_int(table_schema.get_table_id());
            break;
          }
            // key_name
          case OB_APP_MIN_COLUMN_ID + 1: {
            index_name.reset();
            //  get the original short index name
            if (OB_FAIL(ObTableSchema::get_index_name(
                    *allocator_, table_schema.get_table_id(), index_schema->get_table_name_str(), index_name))) {
              SERVER_LOG(WARN, "error get index table name failed", K(ret));
            } else {
              cells[cell_idx].set_varchar(index_name);
              cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            }
            break;
          }
            // seq_in_index
          case OB_APP_MIN_COLUMN_ID + 2: {
            cells[cell_idx].set_int(ft_dep_col_idx_ + 1);
            break;
          }
            // table_schema
          case OB_APP_MIN_COLUMN_ID + 3: {
            cells[cell_idx].set_varchar(database_name);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // table
          case OB_APP_MIN_COLUMN_ID + 4: {
            cells[cell_idx].set_varchar(table_schema.get_table_name_str());
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // non_unique
          case OB_APP_MIN_COLUMN_ID + 5: {
            int64_t non_unique = 0;
            if (INDEX_TYPE_UNIQUE_GLOBAL == index_schema->get_index_type() ||
                INDEX_TYPE_UNIQUE_LOCAL == index_schema->get_index_type()) {
              non_unique = 0;
            } else {
              non_unique = 1;
            }
            cells[cell_idx].set_int(non_unique);
            break;
          }
            // index_schema
          case OB_APP_MIN_COLUMN_ID + 6: {
            cells[cell_idx].set_varchar(database_name);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // column_name
          case OB_APP_MIN_COLUMN_ID + 7: {
            cells[cell_idx].set_varchar(column_schema->get_column_name());
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // collation
          case OB_APP_MIN_COLUMN_ID + 8: {
            cells[cell_idx].set_varchar(ObString("A"));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // cardinality
          case OB_APP_MIN_COLUMN_ID + 9: {
            // TODO: not implemented
            cells[cell_idx].set_null();
            break;
          }
            // sub_part
          case OB_APP_MIN_COLUMN_ID + 10: {
            // TODO : should be number of characters been indexed if prefix index, otherwise, it
            // should be NULL
            cells[cell_idx].reset();  // clear last row result
            if (column_schema->is_prefix_column()) {
              // print prefix index length
              int64_t pos = 0;
              if (OB_FAIL(databuff_printf(buf, buf_len, pos, "%d", column_schema->get_data_length()))) {
                LOG_WARN(
                    "print prefix column data length failed", K(ret), KPC(column_schema), K(buf), K(buf_len), K(pos));
              } else {
                cells[cell_idx].set_varchar(ObString(pos, buf));
                cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
              }
            }
            break;
          }
            // packed
          case OB_APP_MIN_COLUMN_ID + 11: {
            // TODO: not implemented yet
            cells[cell_idx].set_null();
            break;
          }
            // null
          case OB_APP_MIN_COLUMN_ID + 12: {
            if (column_schema->is_rowkey_column()) {
              cells[cell_idx].set_varchar(ObString(""));
            } else if (column_schema->is_nullable()) {
              cells[cell_idx].set_varchar(ObString("YES"));
            } else {
              cells[cell_idx].set_varchar(ObString(""));
            }
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // index_type
          case OB_APP_MIN_COLUMN_ID + 13: {
            cells[cell_idx].set_varchar(ObString("FULLTEXT"));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // comment
          case OB_APP_MIN_COLUMN_ID + 14: {
            // TODO
            cells[cell_idx].set_varchar(ObString(ob_index_status_str(index_schema->get_index_status())));
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
            // index_comment
          case OB_APP_MIN_COLUMN_ID + 15: {
            cells[cell_idx].set_varchar(index_schema->get_comment_str());
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          case OB_APP_MIN_COLUMN_ID + 16: {
            const ObString& is_visible = index_schema->is_index_visible() ? "YES" : "NO";
            cells[cell_idx].set_varchar(is_visible);
            cells[cell_idx].set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset()));
            break;
          }
          default: {
            ret = OB_ERR_UNEXPECTED;
            SERVER_LOG(WARN, "invalid column id", K(ret), K(cell_idx), K(output_column_ids_), K(col_id));
            break;
          }
        }
        if (OB_SUCC(ret)) {
          ++cell_idx;
        }
      }
      ++ft_dep_col_idx_;
    }
  }
  return ret;
}

int ObTableIndex::get_show_column_name(
    const ObTableSchema& table_schema, const ObColumnSchemaV2& column_schema, ObString& column_name)
{
  int ret = OB_SUCCESS;
  if (column_schema.is_prefix_column()) {
    // prefix index column, should get the original column
    ObSEArray<uint64_t, 1> deps_column_ids;
    const ObColumnSchemaV2* deps_column = NULL;
    if (OB_FAIL(column_schema.get_cascaded_column_ids(deps_column_ids))) {
      LOG_WARN("get cascaded column ids from column schema failed", K(ret), K(column_schema));
    } else if (OB_UNLIKELY(deps_column_ids.count() != 1)) {
      ret = OB_ERR_UNEXPECTED;
      LOG_WARN("deps column ids is invalid", K(ret), K(deps_column_ids));
    } else if (OB_ISNULL(deps_column = table_schema.get_column_schema(deps_column_ids.at(0)))) {
      ret = OB_ERR_COLUMN_NOT_FOUND;
      LOG_WARN("deps column not found in table schema", K(ret), K(deps_column_ids.at(0)), K(table_schema));
    } else {
      column_name = deps_column->get_column_name_str();
    }
  } else {
    column_name = column_schema.get_column_name_str();
  }
  return ret;
}
}  // namespace observer
}  // namespace oceanbase
