/* Copyright (C) 2008-2019 Kentoku Shiba
   Copyright (C) 2019-2022 MariaDB corp

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */

#define SPIDER_CONNECT_INFO_MAX_LEN 64
#define SPIDER_CONNECT_INFO_PATH_MAX_LEN FN_REFLEN
#define SPIDER_LONGLONG_LEN 20
#define SPIDER_MAX_KEY_LENGTH 16384

#define SPIDER_SET_CONNS_PARAM(param_name, param_val, conns, link_statuses, conn_link_idx, link_count, link_status) \
  for ( \
    roop_count = spider_conn_link_idx_next(link_statuses, \
      conn_link_idx, -1, link_count, link_status); \
    roop_count < link_count; \
    roop_count = spider_conn_link_idx_next(link_statuses, \
      conn_link_idx, roop_count, link_count, link_status) \
  ) { \
    if (conns[roop_count]) \
      conns[roop_count]->param_name = param_val; \
  }

class ha_spider;
struct st_spider_ft_info
{
  struct _ft_vft *please;
  st_spider_ft_info *next;
  ha_spider *file;
  uint target;
  bool used_in_where;
  float score;
  uint flags;
  uint inx;
  String *key;
};

class ha_spider final : public handler
{
public:
  SPIDER_SHARE       *share;
  ulonglong          spider_thread_id;
  ulonglong          trx_conn_adjustment;
  uint               mem_calc_id;
  const char         *mem_calc_func_name;
  const char         *mem_calc_file_name;
  ulong              mem_calc_line_no;
  ulonglong          *connection_ids;
  char               *conn_keys_first_ptr;
  char               **conn_keys;
  SPIDER_CONN        **conns;
  /*
    Array of indexes of active servers.

    For a spider table or partition with multiple remotes (HA), the
    remotes are divided into n groups of active links, where n is the
    number of "active link count", equal to share->link_count aka
    share->active_link_count. For example, if a spider table has 11
    remotes (i.e. share->all_link_count == 11), and
    share->active_link_count == 3, then we have 3 link groups with
    group 0 consisting of the 0th, 3rd, 6th and 9th remotes and so on:

    group 0: 0, 3, 6, 9
    group 1: 1, 4, 7, 10
    group 2: 2, 5, 8

    conn_link_idx[i] is the "current" remote chosen for the ith group,
    and it can only take a value in the ith group.

    Continue with the example above, at some point, we could end up
    with:

    conn_link_idx[0] == 3
    conn_link_idx[1] == 1
    conn_link_idx[2] == 8

    conn_link_idx is set in spider_trx_set_link_idx_for_all().

    By default, active_link_idx is the same number as all_link_count,
    i.e. 11 in the above example.

    If spider HA is gone (MDEV-28862), this will be no longer needed.

    Typically, to distinguish the ith group and ith link, we use
    variable names link_idx and all_link_idx respectively, so we often
    have

    all_link_idx == conn_link_idx[link_idx]

    spider->conns[link_idx] is created using connection info of the
    `conn_link_idx[link_idx]'th remote.

    When only one of the indexes is used, we simply use variable name
    link_idx
  */
  uint               *conn_link_idx;
  /* A bitmap indicating whether each active server have some higher
  numbered server in the same "group" left to try (can fail over) */
  uchar              *conn_can_fo;
  void               **quick_targets;
  /* indexed on active servers */
  int                *need_mons;
  query_id_t         search_link_query_id;
  int                search_link_idx;
  int                result_link_idx;
  SPIDER_RESULT_LIST result_list;
  spider_string      *blob_buff;
  SPIDER_POSITION    *pushed_pos;
  SPIDER_POSITION    pushed_pos_buf;
  SPIDER_PARTITION_HANDLER *partition_handler;
  /* Whether this ha_spider is the owner of its wide_handler. */
  bool                wide_handler_owner = FALSE;
  SPIDER_WIDE_HANDLER *wide_handler = NULL;

  bool               is_clone;
  ha_spider          *pt_clone_source_handler;
  ha_spider          *pt_clone_last_searcher;
  bool               use_index_merge;

  bool               init_index_handler;
  bool               init_rnd_handler;

  bool               da_status;
  bool               use_spatial_index;

  /*
    Index of the table in FROM tables, for the use of direct
    execution by gbh
  */
  uint                  idx_for_direct_join;
  /*
    Whether using a spider_fields, only applicable to direct
    execution by gbh
  */
  bool                  use_fields;
  /* If use_fields == true, the spider_fields in use for gbh */
  spider_fields         *fields;
  SPIDER_LINK_IDX_CHAIN *link_idx_chain;
  SPIDER_LINK_IDX_CHAIN *result_link_idx_chain;

  /* for mrr */
  bool               mrr_with_cnt;
  uint               multi_range_cnt;
  uint               multi_range_hit_point;
  int                multi_range_num;
  bool               have_second_range;
  KEY_MULTI_RANGE    mrr_second_range;
  spider_string      *mrr_key_buff;
  range_id_t         *multi_range_keys;

  char               *append_tblnm_alias;
  uint               append_tblnm_alias_length;

  ha_spider          *next;

  bool               dml_inited;
  bool               rnd_scan_and_first;
  bool               use_pre_call;
  bool               use_pre_action;
  bool               pre_bitmap_checked;
  bool               bulk_insert;
  bool               info_auto_called;
  bool               auto_inc_temporary;
  int                bulk_size= 0;
  int                direct_dup_insert;
  int                store_error_num;
  uint               dup_key_idx;
  int                select_column_mode;
  bool               pk_update;
  bool               force_auto_increment;
  int                bka_mode;
  int                error_mode;
  ulonglong          store_last_insert_id;

  ulonglong          *db_request_id;
  uchar              *db_request_phase;
  bool               do_direct_update;
  uint               direct_update_kinds;
  spider_index_rnd_init prev_index_rnd_init;
  SPIDER_ITEM_HLD    *direct_aggregate_item_first;
  SPIDER_ITEM_HLD    *direct_aggregate_item_current;
  ha_rows            table_rows;
  ha_checksum        checksum_val;
  bool               checksum_null;
  uint               action_flags;

  /* for fulltext search */
  bool               ft_init_and_first;
  uint               ft_init_idx;
  uint               ft_count;
  bool               ft_init_without_index_init;
  st_spider_ft_info  *ft_first;
  st_spider_ft_info  *ft_current;

  /* for dbton */
  spider_db_handler  **dbton_handler;

  /* for direct limit offset */
  longlong direct_select_offset;
  longlong direct_current_offset;
  longlong direct_select_limit;

  ha_spider();
  ha_spider(
    handlerton *hton,
    TABLE_SHARE *table_arg
  );
  virtual ~ha_spider();
  handler *clone(
    const char *name,
    MEM_ROOT *mem_root
  ) override;
  const char **bas_ext() const;
  int open(
    const char* name,
    int mode,
    uint test_if_locked
  ) override;
  int close() override;
  int check_access_kind_for_connection(
    THD *thd,
    bool write_request
  );
  void check_access_kind(
    THD *thd
  );
  THR_LOCK_DATA **store_lock(
    THD *thd,
    THR_LOCK_DATA **to,
    enum thr_lock_type lock_type
  ) override;
  int external_lock(
    THD *thd,
    int lock_type
  ) override;
  int start_stmt(
    THD *thd,
    thr_lock_type lock_type
  ) override;
  int reset() override;
  int extra(
    enum ha_extra_function operation
  ) override;
  int index_init(uint idx, bool sorted) override;
  int index_end() override;
  int index_read_map(
    uchar *buf,
    const uchar *key,
    key_part_map keypart_map,
    enum ha_rkey_function find_flag
  ) override;
  int index_read_last_map(
    uchar *buf,
    const uchar *key,
    key_part_map keypart_map
  ) override;
  int index_next(
    uchar *buf
  ) override;
  int index_prev(
    uchar *buf
  ) override;
  int index_first(
    uchar *buf
  ) override;
  int index_last(
    uchar *buf
  ) override;
  int index_next_same(
    uchar *buf,
    const uchar *key,
    uint keylen
  ) override;
  int read_range_first(
    const key_range *start_key,
    const key_range *end_key,
    bool eq_range,
    bool sorted
  ) override;
  int read_range_next() override;
  void reset_no_where_cond();
  bool check_no_where_cond();
  ha_rows multi_range_read_info_const(
    uint keyno,
    RANGE_SEQ_IF *seq,
    void *seq_init_param,
    uint n_ranges,
    uint *bufsz,
    uint *flags,
    ha_rows limit,
    Cost_estimate *cost
  ) override;
  ha_rows multi_range_read_info(
    uint keyno,
    uint n_ranges,
    uint keys,
    uint key_parts,
    uint *bufsz,
    uint *flags,
    Cost_estimate *cost
  ) override;
  int multi_range_read_init(
    RANGE_SEQ_IF *seq,
    void *seq_init_param,
    uint n_ranges,
    uint mode,
    HANDLER_BUFFER *buf
  ) override;
  int multi_range_read_next(
    range_id_t *range_info
  ) override;
  int multi_range_read_next_first(
    range_id_t *range_info
  );
  int multi_range_read_next_next(
    range_id_t *range_info
  );
  int rnd_init(bool scan) override;
  int rnd_end() override;
  int rnd_next(
    uchar *buf
  ) override;
  void position(
    const uchar *record
  ) override;
  int rnd_pos(
    uchar *buf,
    uchar *pos
  ) override;
  int cmp_ref(
    const uchar *ref1,
    const uchar *ref2
  ) override;
  int ft_init() override;
  void ft_end() override;
  FT_INFO *ft_init_ext(
    uint flags,
    uint inx,
    String *key
  ) override;
  int ft_read(
    uchar *buf
  ) override;
  int pre_index_read_map(
    const uchar *key,
    key_part_map keypart_map,
    enum ha_rkey_function find_flag,
    bool use_parallel
  ) override;
  int pre_index_first(bool use_parallel) override;
  int pre_index_last(bool use_parallel) override;
  int pre_index_read_last_map(
    const uchar *key,
    key_part_map keypart_map,
    bool use_parallel
  ) override;
  int pre_multi_range_read_next(bool use_parallel) override;
  int pre_read_range_first(
    const key_range *start_key,
    const key_range *end_key,
    bool eq_range,
    bool sorted,
    bool use_parallel
  ) override;
  int pre_ft_read(bool use_parallel) override;
  int pre_rnd_next(bool use_parallel) override;
  int info(
    uint flag
  ) override;
  ha_rows records_in_range(
    uint inx,
    const key_range *start_key,
    const key_range *end_key,
    page_range *pages
  ) override;
  int check_crd();
  int pre_records() override;
  ha_rows records() override;
  int pre_calculate_checksum() override;
  int calculate_checksum() override;
  const char *table_type() const override;
  ulonglong table_flags() const override;
  ulong table_flags_for_partition();
  const char *index_type(uint key_number) override;
  ulong index_flags(uint idx, uint part, bool all_parts) const override;
  uint max_supported_record_length() const override;
  uint max_supported_keys() const override;
  uint max_supported_key_parts() const override;
  uint max_supported_key_length() const override;
  uint max_supported_key_part_length() const override;
  uint8 table_cache_type() override;
  bool need_info_for_auto_inc() override;
  bool can_use_for_auto_inc_init() override;
  int update_auto_increment();
  void get_auto_increment(
    ulonglong offset,
    ulonglong increment,
    ulonglong nb_desired_values,
    ulonglong *first_value,
    ulonglong *nb_reserved_values
  ) override;
  int reset_auto_increment(ulonglong value) override;
  void release_auto_increment() override;
  void start_bulk_insert(ha_rows rows, uint flags) override;
  int end_bulk_insert() override;
  int write_row(const uchar *buf) override;
  void direct_update_init(
    THD *thd,
    bool hs_request
  );
  bool start_bulk_update() override;
  int exec_bulk_update(ha_rows *dup_key_found) override;
  int end_bulk_update() override;
  int bulk_update_row(
    const uchar *old_data,
    const uchar *new_data,
    ha_rows *dup_key_found
  ) override;
  int update_row(
    const uchar *old_data,
    const uchar *new_data
  ) override;
  bool check_direct_update_sql_part(
    st_select_lex *select_lex,
    longlong select_limit,
    longlong offset_limit
  );
  int direct_update_rows_init(List<Item> *update_fields) override;
  int direct_update_rows(ha_rows *update_rows, ha_rows *found_row) override;
  bool start_bulk_delete() override;
  int end_bulk_delete() override;
  int delete_row(const uchar *buf) override;
  bool check_direct_delete_sql_part(
    st_select_lex *select_lex,
    longlong select_limit,
    longlong offset_limit
  );
  int direct_delete_rows_init() override;
  int direct_delete_rows(
    ha_rows *delete_rows
  ) override;
  int delete_all_rows() override;
  int truncate() override;
  IO_AND_CPU_COST scan_time() override;
  IO_AND_CPU_COST rnd_pos_time(ha_rows rows) override;
  IO_AND_CPU_COST keyread_time(uint index, ulong ranges, ha_rows rows,
                               ulonglong blocks) override;
  const key_map *keys_to_use_for_scanning() override;
  ha_rows estimate_rows_upper_bound() override;
  void print_error(
    int error,
    myf errflag
  ) override;
  bool get_error_message(
    int error,
    String *buf
  ) override;
  int create(
    const char *name,
    TABLE *form,
    HA_CREATE_INFO *info
  ) override;
  void update_create_info(
    HA_CREATE_INFO* create_info
  ) override;
  int rename_table(
    const char *from,
    const char *to
  ) override;
  int delete_table(
    const char *name
  ) override;
  bool is_crashed() const override;
#ifdef SPIDER_HANDLER_AUTO_REPAIR_HAS_ERROR
  bool auto_repair(int error) const override;
#else
  bool auto_repair() const;
#endif
  int disable_indexes(
    key_map map, bool persist
  ) override;
  int enable_indexes(
    key_map map, bool persist
  ) override;
  int check(
    THD* thd,
    HA_CHECK_OPT* check_opt
  ) override;
  int repair(
    THD* thd,
    HA_CHECK_OPT* check_opt
  ) override;
  bool check_and_repair(
    THD *thd
  ) override;
  int analyze(
    THD* thd,
    HA_CHECK_OPT* check_opt
  ) override;
  int optimize(
    THD* thd,
    HA_CHECK_OPT* check_opt
  ) override;
  bool is_fatal_error(
    int error_num,
    uint flags
  ) override;
  Field *field_exchange(
    Field *field
  );
  const COND *cond_push(
    const COND* cond
  ) override;
  void cond_pop() override;
  int info_push(uint info_type, void *info) override;
  void return_record_by_parent() override;
  TABLE *get_table();
  void set_ft_discard_bitmap();
  void set_searched_bitmap();
  void set_clone_searched_bitmap();
  void set_searched_bitmap_from_item_list();
  void set_select_column_mode();
  void check_select_column(bool rnd);
  bool check_and_start_bulk_update(
    spider_bulk_upd_start bulk_upd_start
  );
  int check_and_end_bulk_update(
    spider_bulk_upd_start bulk_upd_start
  );
  uint check_partitioned();
  void check_direct_order_limit();
  void check_distinct_key_query();
  bool is_sole_projection_field(
    uint16 field_index
  );
  int drop_tmp_tables();
  void set_error_mode();
  void backup_error_status();
  int check_error_mode(
    int error_num
  );
  int check_error_mode_eof(
    int error_num
  );
  int index_read_map_internal(
    uchar *buf,
    const uchar *key,
    key_part_map keypart_map,
    enum ha_rkey_function find_flag
  );
  int index_read_last_map_internal(
    uchar *buf,
    const uchar *key,
    key_part_map keypart_map
  );
  int index_first_internal(uchar *buf);
  int index_last_internal(uchar *buf);
  int read_range_first_internal(
    uchar *buf,
    const key_range *start_key,
    const key_range *end_key,
    bool eq_range,
    bool sorted
  );
  int ft_read_internal(uchar *buf);
  int rnd_next_internal(uchar *buf);
  void check_pre_call(
    bool use_parallel
  );
  void check_insert_dup_update_pushdown();
  void sync_from_clone_source_base(
    ha_spider *spider
  );
  void set_first_link_idx();
  void reset_first_link_idx();
  int reset_sql_sql(
    ulong sql_type
  );
  int append_tmp_table_and_sql_for_bka(
    const key_range *start_key
  );
  int reuse_tmp_table_and_sql_for_bka();
  int append_union_table_and_sql_for_bka(
    const key_range *start_key
  );
  int reuse_union_table_and_sql_for_bka();
  int append_insert_sql_part();
  int append_update_sql_part();
  int append_update_set_sql_part();
  int append_direct_update_set_sql_part();
  int append_dup_update_pushdown_sql_part(
    const char *alias,
    uint alias_length
  );
  int append_update_columns_sql_part(
    const char *alias,
    uint alias_length
  );
  int check_update_columns_sql_part();
  int append_delete_sql_part();
  int append_select_sql_part(
    ulong sql_type
  );
  int append_table_select_sql_part(
    ulong sql_type
  );
  int append_key_select_sql_part(
    ulong sql_type,
    uint idx
  );
  int append_minimum_select_sql_part(
    ulong sql_type
  );
  int append_from_sql_part(
    ulong sql_type
  );
  int append_hint_after_table_sql_part(
    ulong sql_type
  );
  void set_where_pos_sql(
    ulong sql_type
  );
  void set_where_to_pos_sql(
    ulong sql_type
  );
  int check_item_type_sql(
    Item *item
  );
  int append_values_connector_sql_part(
    ulong sql_type
  );
  int append_values_terminator_sql_part(
    ulong sql_type
  );
  int append_union_table_connector_sql_part(
    ulong sql_type
  );
  int append_union_table_terminator_sql_part(
    ulong sql_type
  );
  int append_key_column_values_sql_part(
    const key_range *start_key,
    ulong sql_type
  );
  int append_key_column_values_with_name_sql_part(
    const key_range *start_key,
    ulong sql_type
  );
  int append_key_where_sql_part(
    const key_range *start_key,
    const key_range *end_key,
    ulong sql_type
  );
  int append_match_where_sql_part(
    ulong sql_type
  );
  int append_condition_sql_part(
    const char *alias,
    uint alias_length,
    ulong sql_type,
    bool test_flg
  );
  int append_sum_select_sql_part(
    ulong sql_type,
    const char *alias,
    uint alias_length
  );
  int append_match_select_sql_part(
    ulong sql_type,
    const char *alias,
    uint alias_length
  );
  void set_order_pos_sql(
    ulong sql_type
  );
  void set_order_to_pos_sql(
    ulong sql_type
  );
  int append_group_by_sql_part(
    const char *alias,
    uint alias_length,
    ulong sql_type
  );
  int append_key_order_for_merge_with_alias_sql_part(
    const char *alias,
    uint alias_length,
    ulong sql_type
  );
  int append_key_order_for_direct_order_limit_with_alias_sql_part(
    const char *alias,
    uint alias_length,
    ulong sql_type
  );
  int append_key_order_with_alias_sql_part(
    const char *alias,
    uint alias_length,
    ulong sql_type
  );
  int append_limit_sql_part(
    longlong offset,
    longlong limit,
    ulong sql_type
  );
  int reappend_limit_sql_part(
    longlong offset,
    longlong limit,
    ulong sql_type
  );
  int append_insert_terminator_sql_part(
    ulong sql_type
  );
  int append_insert_values_sql_part(
    ulong sql_type
  );
  int append_into_sql_part(
    ulong sql_type
  );
  void set_insert_to_pos_sql(
    ulong sql_type
  );
  bool is_bulk_insert_exec_period(
    bool bulk_end
  );
  int append_select_lock_sql_part(
    ulong sql_type
  );
  int append_union_all_start_sql_part(
    ulong sql_type
  );
  int append_union_all_sql_part(
    ulong sql_type
  );
  int append_union_all_end_sql_part(
    ulong sql_type
  );
  int append_multi_range_cnt_sql_part(
    ulong sql_type,
    uint multi_range_cnt,
    bool with_comma
  );
  int append_multi_range_cnt_with_name_sql_part(
    ulong sql_type,
    uint multi_range_cnt
  );
  int append_delete_all_rows_sql_part(
    ulong sql_type
  );
  int append_update_sql(
    const TABLE *table,
    my_ptrdiff_t ptr_diff,
    bool bulk
  );
  int append_delete_sql(
    const TABLE *table,
    my_ptrdiff_t ptr_diff,
    bool bulk
  );
  bool sql_is_filled_up(
    ulong sql_type
  );
  bool sql_is_empty(
    ulong sql_type
  );
  bool support_multi_split_read_sql();
  bool support_bulk_update_sql();
  int bulk_tmp_table_insert();
  int bulk_tmp_table_end_bulk_insert();
  int bulk_tmp_table_rnd_init();
  int bulk_tmp_table_rnd_next();
  int bulk_tmp_table_rnd_end();
  int mk_bulk_tmp_table_and_bulk_start();
  void rm_bulk_tmp_table();
  bool bulk_tmp_table_created();
  int print_item_type(
    Item *item,
    spider_string *str,
    const char *alias,
    uint alias_length
  );
  int init_union_table_name_pos_sql();
  int set_union_table_name_pos_sql();
  int append_lock_tables_list();
  int lock_tables();
  int dml_init();
private:
  void init_fields();
};


/* This is a hack for ASAN
 * Libraries such as libxml2 and libodbc do not like being unloaded before
 * exit and will show as a leak in ASAN with no stack trace (as the plugin
 * has been unloaded from memory).
 *
 * The below is designed to trick the compiler into adding a "UNIQUE" symbol
 * which can be seen using:
 * readelf -s storage/spider/ha_spider.so | grep UNIQUE
 *
 * Having this symbol means that the plugin remains in memory after dlclose()
 * has been called. Thereby letting the libraries clean up properly.
 */
#if defined(__SANITIZE_ADDRESS__)
__attribute__((__used__))
inline int dummy(void)
{
  static int d;
  d++;
  return d;
}
#endif
