int ha_maria::delete_all_rows()
{
  THD *thd= table->in_use;
  TRN *trn= file->trn;
  CHECK_UNTIL_WE_FULLY_IMPLEMENTED_VERSIONING("TRUNCATE in WRITE CONCURRENT");
#ifdef EXTRA_DEBUG
  if (trn && ! (trnman_get_flags(trn) & TRN_STATE_INFO_LOGGED))
  {
    trnman_set_flags(trn, trnman_get_flags(trn) | TRN_STATE_INFO_LOGGED |
                     TRN_STATE_TABLES_CAN_CHANGE);
    (void) translog_log_debug_info(trn, LOGREC_DEBUG_INFO_QUERY,
                                   (uchar*) thd->query(), thd->query_length());
  }
#endif
  /*
    If we are under LOCK TABLES, we have to do a commit as
    delete_all_rows() can't be rolled back
  */
  if (table->in_use->locked_tables_mode && trn &&
      trnman_has_locked_tables(trn))
  {
    int error;
    if ((error= implicit_commit(thd, 1)))
      return error;
  }

  /* Note that this can't be rolled back */
  return maria_delete_all_rows(file);
}


const char *ha_maria::index_type(uint key_number)
{
  return ((table->key_info[key_number].flags & HA_FULLTEXT) ?
          "FULLTEXT" :
          (table->key_info[key_number].flags & HA_SPATIAL) ?
          "SPATIAL" :
          (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ?
          "RTREE" : "BTREE");
}

MDEV-25787 Bug report: crash on SELECT DISTINCT thousands_blob_fields

fix a debug assert to account for not opened temp tables


int ha_maria::rnd_end()
{
  ds_mrr.dsmrr_close();
  /* Safe to call even if we don't have started a scan */
  maria_scan_end(file);
  return 0;
}


int ha_maria::index_prev(uchar * buf)
{
  DBUG_ASSERT(inited == INDEX);
  register_handler(file);
  int error= maria_rprev(file, buf, active_index);
  return error;
}


int ha_maria::remember_rnd_pos()
{
  register_handler(file);
  return (*file->s->scan_remember_pos)(file, &remember_pos);
}


int ha_maria::optimize(THD * thd, HA_CHECK_OPT *check_opt)
{
  int error;
  HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);

  if (!file || !param)
    return HA_ADMIN_INTERNAL_ERROR;

  maria_chk_init(param);
  param->thd= thd;
  param->op_name= "optimize";
  param->testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
                   T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX);
  param->orig_sort_buffer_length= THDVAR(thd, sort_buffer_size);
  thd_progress_init(thd, 1);
  if ((error= repair(thd, param, 1)) && param->retry_repair)
  {
    sql_print_warning("Warning: Optimize table got errno %d on %s.%s, retrying",
                      my_errno, param->db_name, param->table_name);
    param->testflag &= ~T_REP_BY_SORT;
    error= repair(thd, param, 0);
  }
  thd_progress_end(thd);
  return error;
}


int ha_maria::preload_keys(THD * thd, HA_CHECK_OPT *check_opt)
{
  ulonglong map;
  TABLE_LIST *table_list= table->pos_in_table_list;

  DBUG_ENTER("ha_maria::preload_keys");

  table->keys_in_use_for_query.clear_all();

  if (table_list->process_index_hints(table))
    DBUG_RETURN(HA_ADMIN_FAILED);

  map= ~(ulonglong) 0;
  /* Check validity of the index references */
  if (!table->keys_in_use_for_query.is_clear_all())
    /* use all keys if there's no list specified by the user through hints */
    map= table->keys_in_use_for_query.to_ulonglong();

  maria_extra(file, HA_EXTRA_PRELOAD_BUFFER_SIZE,
              (void*) &thd->variables.preload_buff_size);

  int error;

  if ((error= maria_preload(file, map, table_list->ignore_leaves)))
  {
    char buf[MYSQL_ERRMSG_SIZE+20];
    const char *errmsg;

    switch (error) {
    case HA_ERR_NON_UNIQUE_BLOCK_SIZE:
      errmsg= "Indexes use different block sizes";
      break;
    case HA_ERR_OUT_OF_MEM:
      errmsg= "Failed to allocate buffer";
      break;
    default:
      my_snprintf(buf, sizeof(buf),
                  "Failed to read from index file (errno: %d)", my_errno);
      errmsg= buf;
    }

    HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
    if (!param)
      return HA_ADMIN_INTERNAL_ERROR;

    maria_chk_init(param);
    param->thd= thd;
    param->op_name= "preload_keys";
    param->db_name= table->s->db.str;
    param->table_name= table->s->table_name.str;
    param->testflag= 0;
    _ma_check_print_error(param, "%s", errmsg);
    DBUG_RETURN(HA_ADMIN_FAILED);
  }
  DBUG_RETURN(HA_ADMIN_OK);
}


int ha_maria::end_bulk_insert()
{
  int first_error, error;
  my_bool abort= file->s->deleting;
  DBUG_ENTER("ha_maria::end_bulk_insert");

  if ((first_error= maria_end_bulk_insert(file, abort)))
    abort= 1;

  if ((error= maria_extra(file, HA_EXTRA_NO_CACHE, 0)))
  {
    first_error= first_error ? first_error : error;
    abort= 1;
  }

  if (!abort && can_enable_indexes)
    if ((error= enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE)))
      first_error= first_error ? first_error : error;

  if (bulk_insert_single_undo != BULK_INSERT_NONE)
  {
    /*
      Table was transactional just before start_bulk_insert().
      No need to flush pages if we did a repair (which already flushed).
    */
    if ((error= _ma_reenable_logging_for_table(file,
                                               bulk_insert_single_undo ==
                                               BULK_INSERT_SINGLE_UNDO_AND_NO_REPAIR)))
      first_error= first_error ? first_error : error;
    bulk_insert_single_undo= BULK_INSERT_NONE;  // Safety
  }
  can_enable_indexes= 0;
  DBUG_RETURN(first_error);
}


int ha_maria::delete_table(const char *name)
{
  THD *thd= current_thd;
  (void) translog_log_debug_info(0, LOGREC_DEBUG_INFO_QUERY,
                                 (uchar*) thd->query(), thd->query_length());
  return maria_delete_table(name);
}


static int maria_hton_panic(handlerton *hton, ha_panic_function flag)
{
  /* If no background checkpoints, we need to do one now */
  int ret=0;

  if (!checkpoint_interval && !aria_readonly)
    ret= ma_checkpoint_execute(CHECKPOINT_FULL, FALSE);

  ret|= maria_panic(flag);

  maria_hton= 0;
  return ret;
}


int ha_maria::info(uint flag)
{
  MARIA_INFO maria_info;
  char name_buff[FN_REFLEN];

  (void) maria_status(file, &maria_info, flag);
  if (flag & HA_STATUS_VARIABLE)
  {
    stats.records=           maria_info.records;
    stats.deleted=           maria_info.deleted;
    stats.data_file_length=  maria_info.data_file_length;
    stats.index_file_length= maria_info.index_file_length;
    stats.delete_length=     maria_info.delete_length;
    stats.check_time=        maria_info.check_time;
    stats.mean_rec_length=   maria_info.mean_reclength;
    stats.checksum=          file->state->checksum;
  }
  if (flag & HA_STATUS_CONST)
  {
    TABLE_SHARE *share= table->s;
    stats.max_data_file_length=  maria_info.max_data_file_length;
    stats.max_index_file_length= maria_info.max_index_file_length;
    stats.create_time= maria_info.create_time;
    ref_length= maria_info.reflength;
    share->db_options_in_use= maria_info.options;
    stats.block_size= maria_block_size;
    stats.mrr_length_per_rec= maria_info.reflength + 8; // 8 = MY_MAX(sizeof(void *))

    /* Update share */
    share->keys_in_use.set_prefix(share->keys);
    share->keys_in_use.intersect_extended(maria_info.key_map);
    share->keys_for_keyread.intersect(share->keys_in_use);
    share->db_record_offset= maria_info.record_offset;
    if (share->key_parts)
    {
      ulong *to= table->key_info[0].rec_per_key, *end;
      double *from= maria_info.rec_per_key;
      for (end= to+ share->key_parts ; to < end ; to++, from++)
        *to= (ulong) (*from + 0.5);
    }

    /*
       Set data_file_name and index_file_name to point at the symlink value
       if table is symlinked (Ie;  Real name is not same as generated name)
    */
    data_file_name= index_file_name= 0;
    fn_format(name_buff, file->s->open_file_name.str, "", MARIA_NAME_DEXT,
              MY_APPEND_EXT | MY_UNPACK_FILENAME);
    if (strcmp(name_buff, maria_info.data_file_name) &&
        maria_info.data_file_name[0])
      data_file_name= maria_info.data_file_name;
    fn_format(name_buff, file->s->open_file_name.str, "", MARIA_NAME_IEXT,
              MY_APPEND_EXT | MY_UNPACK_FILENAME);
    if (strcmp(name_buff, maria_info.index_file_name) &&
        maria_info.index_file_name[0])
      index_file_name=maria_info.index_file_name;
  }
  if (flag & HA_STATUS_ERRKEY)
  {
    errkey= maria_info.errkey;
    my_store_ptr(dup_ref, ref_length, maria_info.dup_key_pos);
  }
  if (flag & HA_STATUS_TIME)
    stats.update_time= maria_info.update_time;
  if (flag & HA_STATUS_AUTO)
    stats.auto_increment_value= maria_info.auto_increment;

  return 0;
}


int ha_maria::analyze(THD *thd, HA_CHECK_OPT * check_opt)
{
  int error= 0;
  HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
  MARIA_SHARE *share= file->s;
  const char *old_proc_info;

  if (!param)
    return HA_ADMIN_INTERNAL_ERROR;

  maria_chk_init(param);
  param->thd= thd;
  param->op_name= "analyze";
  param->db_name= table->s->db.str;
  param->table_name= table->alias.c_ptr();
  param->testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
                   T_DONT_CHECK_CHECKSUM);
  param->using_global_keycache= 1;
  param->stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);

  if (!(share->state.changed & STATE_NOT_ANALYZED))
    return HA_ADMIN_ALREADY_DONE;

  old_proc_info= thd_proc_info(thd, "Scanning");
  thd_progress_init(thd, 1);
  error= maria_chk_key(param, file);
  if (!error)
  {
    mysql_mutex_lock(&share->intern_lock);
    error= maria_update_state_info(param, file, UPDATE_STAT);
    mysql_mutex_unlock(&share->intern_lock);
  }
  else if (!maria_is_crashed(file) && !thd->killed)
    maria_mark_crashed(file);
  thd_proc_info(thd, old_proc_info);
  thd_progress_end(thd);
  return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK;
}


int ha_maria::index_read_last_map(uchar * buf, const uchar * key,
				  key_part_map keypart_map)
{
  DBUG_ENTER("ha_maria::index_read_last_map");
  DBUG_ASSERT(inited == INDEX);
  register_handler(file);
  int error= maria_rkey(file, buf, active_index, key, keypart_map,
                        HA_READ_PREFIX_LAST);
  DBUG_RETURN(error);
}


int ha_maria::extra(enum ha_extra_function operation)
{
  int tmp;
  TRN *old_trn= file->trn;
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD)
    return 0;
#ifdef NOT_USED
  if (operation == HA_EXTRA_MMAP && !opt_maria_use_mmap)
    return 0;
#endif
  if (operation == HA_EXTRA_WRITE_CACHE && has_long_unique())
    return 0;

  /*
    We have to set file->trn here because in some cases we call
    extern_lock(F_UNLOCK) (which resets file->trn) followed by maria_close()
    without calling commit/rollback in between.  If file->trn is not set
    we can't remove file->share from the transaction list in the extra() call.

    In current code we don't have to do this for HA_EXTRA_PREPARE_FOR_RENAME
    as this is only used the intermediate table used by ALTER TABLE which
    is not part of the transaction (it's not in the TRN list). Better to
    keep this for now, to not break anything in a stable release.
    When HA_EXTRA_PREPARE_FOR_RENAME is not handled below, we can change
    the warnings in _ma_remove_table_from_trnman() to asserts.

    table->in_use is not set in the case this is a done as part of closefrm()
    as part of drop table.
  */

  if (file->s->now_transactional && table->in_use &&
      (operation == HA_EXTRA_PREPARE_FOR_DROP ||
       operation == HA_EXTRA_PREPARE_FOR_RENAME ||
       operation == HA_EXTRA_PREPARE_FOR_FORCED_CLOSE))
  {
    THD *thd= table->in_use;
    file->trn= THD_TRN;
  }
  DBUG_ASSERT(file->s->base.born_transactional || file->trn == 0 ||
              file->trn == &dummy_transaction_object);

  tmp= maria_extra(file, operation, 0);
  /*
    Restore trn if it was changed above.
    Note that table could be removed from trn->used_tables and
    trn->used_instances if trn was set and some of the above operations
    was used. This is ok as the table should not be part of any transaction
    after this and thus doesn't need to be part of any of the above lists.
  */
  file->trn= old_trn;
  return tmp;
}


int ha_maria::zerofill(THD * thd, HA_CHECK_OPT *check_opt)
{
  int error;
  HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
  TRN *old_trn;
  MARIA_SHARE *share= file->s;

  if (!file || !param)
    return HA_ADMIN_INTERNAL_ERROR;

  unmap_file(file);
  old_trn= file->trn;
  maria_chk_init(param);
  param->thd= thd;
  param->op_name= "zerofill";
  param->testflag= check_opt->flags | T_SILENT | T_ZEROFILL;
  param->orig_sort_buffer_length= THDVAR(thd, sort_buffer_size);
  param->db_name= table->s->db.str;
  param->table_name= table->alias.c_ptr();

  error=maria_zerofill(param, file, share->open_file_name.str);

  /* Reset trn, that may have been set by repair */
  if (old_trn && old_trn != file->trn)
    _ma_set_trn_for_table(file, old_trn);

  if (!error)
  {
    TrID create_trid= trnman_get_min_safe_trid();
    mysql_mutex_lock(&share->intern_lock);
    share->state.changed|= STATE_NOT_MOVABLE;
    maria_update_state_info(param, file, UPDATE_TIME | UPDATE_OPEN_COUNT);
    _ma_update_state_lsns_sub(share, LSN_IMPOSSIBLE, create_trid,
                              TRUE, TRUE);
    mysql_mutex_unlock(&share->intern_lock);
  }
  return error;
}


int ha_maria::rnd_pos(uchar *buf, uchar *pos)
{
  register_handler(file);
  int error= maria_rrnd(file, buf, my_get_ptr(pos, ref_length));
  return error;
}


int ha_maria::extra_opt(enum ha_extra_function operation, ulong cache_size)
{
  if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
    return 0;
  return maria_extra(file, operation, (void*) &cache_size);
}


THR_LOCK_DATA **ha_maria::store_lock(THD *thd,
                                     THR_LOCK_DATA **to,
                                     enum thr_lock_type lock_type)
{
  /* Test if we can fix test below */
  DBUG_ASSERT(lock_type != TL_UNLOCK &&
              (lock_type == TL_IGNORE || file->lock.type == TL_UNLOCK));
  if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
  {
    const enum enum_sql_command sql_command= thd->lex->sql_command;
    /*
      We have to disable concurrent inserts for INSERT ... SELECT or
      INSERT/UPDATE/DELETE with sub queries if we are using statement based
      logging.  We take the safe route here and disable this for all commands
      that only does reading that are not SELECT.
    */
    if (lock_type <= TL_READ_HIGH_PRIORITY &&
        !thd->is_current_stmt_binlog_format_row() &&
        (sql_command != SQLCOM_SELECT &&
         sql_command != SQLCOM_LOCK_TABLES) &&
        (thd->variables.option_bits & OPTION_BIN_LOG) &&
        mysql_bin_log.is_open())
      lock_type= TL_READ_NO_INSERT;
    else if (lock_type == TL_WRITE_CONCURRENT_INSERT)
    {
      const enum enum_duplicates duplicates= thd->lex->duplicates;
      /*
        Explanation for the 3 conditions below, in order:

        - Bulk insert may use repair, which will cause problems if other
        threads try to read/insert to the table: disable versioning.
        Note that our read of file->state->records is incorrect, as such
        variable may have changed when we come to start_bulk_insert() (worse
        case: we see != 0 so allow versioning, start_bulk_insert() sees 0 and
        uses repair). This is prevented because start_bulk_insert() will not
        try repair if we enabled versioning.
        - INSERT SELECT ON DUPLICATE KEY UPDATE comes here with
        TL_WRITE_CONCURRENT_INSERT but shouldn't because it can do
        update/delete of a row and versioning doesn't support that
        - same for LOAD DATA CONCURRENT REPLACE.
      */
      if ((file->state->records == 0) ||
          (sql_command == SQLCOM_INSERT_SELECT && duplicates == DUP_UPDATE) ||
          (sql_command == SQLCOM_LOAD && duplicates == DUP_REPLACE))
        lock_type= TL_WRITE;
    }
    file->lock.type= lock_type;
  }
  *to++= &file->lock;
  return to;
}


int ha_maria::rename_table(const char *from, const char *to)
{
  THD *thd= current_thd;
  (void) translog_log_debug_info(0, LOGREC_DEBUG_INFO_QUERY,
                                 (uchar*) thd->query(), thd->query_length());
  return maria_rename(from, to);
}


static handler *maria_create_handler(handlerton *hton,
                                     TABLE_SHARE * table,
                                     MEM_ROOT *mem_root)
{
  return new (mem_root) ha_maria(hton, table);
}


int ha_maria::index_last(uchar * buf)
{
  DBUG_ASSERT(inited == INDEX);
  register_handler(file);
  int error= maria_rlast(file, buf, active_index);
  return error;
}


static int maria_commit(handlerton *hton __attribute__ ((unused)),
                        THD *thd, bool all)
{
  TRN *trn= THD_TRN;
  int res= 0;
  MARIA_HA *used_instances;
  DBUG_ENTER("maria_commit");

  /* No commit inside lock_tables() */
  if ((!trn ||
       thd->locked_tables_mode == LTM_LOCK_TABLES ||
       thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
    DBUG_RETURN(0);

  /* statement or transaction ? */
  if ((thd->variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
      !all)
    DBUG_RETURN(0); // end of statement

  used_instances= (MARIA_HA*) trn->used_instances;
  trnman_reset_locked_tables(trn, 0);
  trnman_set_flags(trn, trnman_get_flags(trn) & ~TRN_STATE_INFO_LOGGED);
  trn->used_instances= 0;
  if (ma_commit(trn))
    res= HA_ERR_COMMIT_ERROR;
  reset_thd_trn(thd, used_instances);
  thd_set_ha_data(thd, maria_hton, 0);
  DBUG_RETURN(res);
}


int _ma_killed_ptr(HA_CHECK *param)
{
  if (likely(thd_killed((THD*)param->thd)) == 0)
    return 0;
  my_errno= HA_ERR_ABORTED_BY_USER;
  return 1;
}


static void reset_thd_trn(THD *thd, MARIA_HA *first_table)
{
  DBUG_ENTER("reset_thd_trn");
  thd_set_ha_data(thd, maria_hton, 0);
  MARIA_HA *next;
  for (MARIA_HA *table= first_table; table ; table= next)
  {
    next= table->trn_next;
    _ma_reset_trn_for_table(table);

    /*
      If table has changed by this statement, invalidate it from the query
      cache
    */
    if (table->row_changes != table->start_row_changes)
    {
      table->start_row_changes= table->row_changes;
      DBUG_ASSERT(table->s->chst_invalidator != NULL);
      (*table->s->chst_invalidator)(table->s->data_file_name.str);
    }
  }
  DBUG_VOID_RETURN;
}


void ha_maria::get_auto_increment(ulonglong offset, ulonglong increment,
                                  ulonglong nb_desired_values,
                                  ulonglong *first_value,
                                  ulonglong *nb_reserved_values)
{
  ulonglong nr;
  int error;
  uchar key[MARIA_MAX_KEY_BUFF];

  if (!table->s->next_number_key_offset)
  {                                             // Autoincrement at key-start
    ha_maria::info(HA_STATUS_AUTO);
    *first_value= stats.auto_increment_value;
    /* Maria has only table-level lock for now, so reserves to +inf */
    *nb_reserved_values= ULONGLONG_MAX;
    return;
  }

  /* it's safe to call the following if bulk_insert isn't on */
  maria_flush_bulk_insert(file, table->s->next_number_index);

  (void) extra(HA_EXTRA_KEYREAD);
  key_copy(key, table->record[0],
           table->key_info + table->s->next_number_index,
           table->s->next_number_key_offset);
  error= maria_rkey(file, table->record[1], (int) table->s->next_number_index,
                    key, make_prev_keypart_map(table->s->next_number_keypart),
                    HA_READ_PREFIX_LAST);
  if (error)
    nr= 1;
  else
  {
    /* Get data from record[1] */
    nr= ((ulonglong) table->next_number_field->
         val_int_offset(table->s->rec_buff_length) + 1);
  }
  extra(HA_EXTRA_NO_KEYREAD);
  *first_value= nr;
  /*
    MySQL needs to call us for next row: assume we are inserting ("a",null)
    here, we return 3, and next this statement will want to insert ("b",null):
    there is no reason why ("b",3+1) would be the good row to insert: maybe it
    already exists, maybe 3+1 is too large...
  */
  *nb_reserved_values= 1;
}


maria_declare_plugin(aria)
{
  MYSQL_STORAGE_ENGINE_PLUGIN,
  &maria_storage_engine,
  "Aria",
  "MariaDB Corporation Ab",
  "Crash-safe tables with MyISAM heritage. Used for internal temporary tables and privilege tables",
  PLUGIN_LICENSE_GPL,
  ha_maria_init,                /* Plugin Init      */
  NULL,                         /* Plugin Deinit    */
  0x0105,                       /* 1.5              */
  status_variables,             /* status variables */
  system_variables,             /* system variables */
  "1.5",                        /* string version   */
  MariaDB_PLUGIN_MATURITY_STABLE /* maturity         */
}


int ha_maria::repair(THD *thd, HA_CHECK *param, bool do_optimize)
{
  int error= 0;
  ulonglong local_testflag= param->testflag;
  bool optimize_done= !do_optimize, statistics_done= 0, full_repair_done= 0;
  const char *old_proc_info= thd->proc_info;
  char fixed_name[FN_REFLEN];
  MARIA_SHARE *share= file->s;
  ha_rows rows= file->state->records;
  TRN *old_trn= file->trn;
  my_bool locking= 0;
  DBUG_ENTER("ha_maria::repair");

  /*
    Normally this method is entered with a properly opened table. If the
    repair fails, it can be repeated with more elaborate options. Under
    special circumstances it can happen that a repair fails so that it
    closed the data file and cannot re-open it. In this case file->dfile
    is set to -1. We must not try another repair without an open data
    file. (Bug #25289)
  */
  if (file->dfile.file == -1)
  {
    sql_print_information("Retrying repair of: '%s' failed. "
                          "Please try REPAIR EXTENDED or aria_chk",
                          table->s->path.str);
    DBUG_RETURN(HA_ADMIN_FAILED);
  }

  /*
    If transactions was not enabled for a transactional table then
    file->s->status is not up to date. This is needed for repair_by_sort
    to work
  */
  if (share->base.born_transactional && !share->now_transactional)
    _ma_copy_nontrans_state_information(file);

  param->db_name= table->s->db.str;
  param->table_name= table->alias.c_ptr();
  param->tmpfile_createflag= O_RDWR | O_TRUNC;
  param->using_global_keycache= 1;
  param->thd= thd;
  param->tmpdir= &mysql_tmpdir_list;
  param->out_flag= 0;
  share->state.dupp_key= MI_MAX_KEY;
  strmov(fixed_name, share->open_file_name.str);
  unmap_file(file);

  /*
    Don't lock tables if we have used LOCK TABLE or if we come from
    enable_index()
  */
  if (!thd->locked_tables_mode && ! (param->testflag & T_NO_LOCKS))
  {
    locking= 1;
    if (maria_lock_database(file, table->s->tmp_table ? F_EXTRA_LCK : F_WRLCK))
    {
      _ma_check_print_error(param, ER_THD(thd, ER_CANT_LOCK), my_errno);
      DBUG_RETURN(HA_ADMIN_FAILED);
    }
  }

  if (!do_optimize ||
      (((share->data_file_type == BLOCK_RECORD) ?
        (share->state.changed & STATE_NOT_OPTIMIZED_ROWS) :
        (file->state->del ||
         share->state.split != file->state->records)) &&
       (!(param->testflag & T_QUICK) ||
        (share->state.changed & (STATE_NOT_OPTIMIZED_KEYS |
                                 STATE_NOT_OPTIMIZED_ROWS)))))
  {
    ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ?
                        maria_get_mask_all_keys_active(share->base.keys) :
                        share->state.key_map);
    ulonglong save_testflag= param->testflag;
    if (maria_test_if_sort_rep(file, file->state->records, key_map, 0) &&
        (local_testflag & T_REP_BY_SORT))
    {
      local_testflag |= T_STATISTICS;
      param->testflag |= T_STATISTICS;           // We get this for free
      statistics_done= 1;
      /* TODO: Remove BLOCK_RECORD test when parallel works with blocks */
      if (THDVAR(thd,repair_threads) > 1 &&
          share->data_file_type != BLOCK_RECORD)
      {
        char buf[40];
        /* TODO: respect maria_repair_threads variable */
        my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
        thd_proc_info(thd, buf);
        param->testflag|= T_REP_PARALLEL;
        error= maria_repair_parallel(param, file, fixed_name,
                                     MY_TEST(param->testflag & T_QUICK));
        /* to reset proc_info, as it was pointing to local buffer */
        thd_proc_info(thd, "Repair done");
      }
      else
      {
        thd_proc_info(thd, "Repair by sorting");
        param->testflag|= T_REP_BY_SORT;
        error= maria_repair_by_sort(param, file, fixed_name,
                                    MY_TEST(param->testflag & T_QUICK));
      }
      if (error && file->create_unique_index_by_sort &&
          share->state.dupp_key != MAX_KEY)
      {
        my_errno= HA_ERR_FOUND_DUPP_KEY;
        print_keydup_error(table, &table->key_info[share->state.dupp_key],
                           MYF(0));
      }
    }
    else
    {
      thd_proc_info(thd, "Repair with keycache");
      param->testflag &= ~(T_REP_BY_SORT | T_REP_PARALLEL);
      error= maria_repair(param, file, fixed_name,
                          MY_TEST(param->testflag & T_QUICK));
    }
    param->testflag= save_testflag | (param->testflag & T_RETRY_WITHOUT_QUICK);
    optimize_done= 1;
    /*
      set full_repair_done if we re-wrote all rows and all keys
      (and thus removed all transid's from the table
    */
    full_repair_done= !MY_TEST(param->testflag & T_QUICK);
  }
  if (!error)
  {
    if ((local_testflag & T_SORT_INDEX) &&
        (share->state.changed & STATE_NOT_SORTED_PAGES))
    {
      optimize_done= 1;
      thd_proc_info(thd, "Sorting index");
      error= maria_sort_index(param, file, fixed_name);
    }
    if (!error && !statistics_done && (local_testflag & T_STATISTICS))
    {
      if (share->state.changed & STATE_NOT_ANALYZED)
      {
        optimize_done= 1;
        thd_proc_info(thd, "Analyzing");
        error= maria_chk_key(param, file);
      }
      else
        local_testflag &= ~T_STATISTICS;        // Don't update statistics
    }
  }
  thd_proc_info(thd, "Saving state");
  if (full_repair_done && !error &&
      !(param->testflag & T_NO_CREATE_RENAME_LSN))
  {
    /* Set trid (needed if the table was moved from another system) */
    share->state.create_trid= trnman_get_min_safe_trid();
  }
  mysql_mutex_lock(&share->intern_lock);
  if (!error)
  {
    if ((share->state.changed & STATE_CHANGED) || maria_is_crashed(file))
    {
      DBUG_PRINT("info", ("Resetting crashed state"));
      share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED_FLAGS |
                               STATE_IN_REPAIR | STATE_MOVED);
      file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
    }
    /*
      repair updates share->state.state. Ensure that file->state is up to date
    */
    if (file->state != &share->state.state)
      *file->state= share->state.state;

    if (share->base.auto_key)
      _ma_update_auto_increment_key(param, file, 1);
    if (optimize_done)
      error= maria_update_state_info(param, file,
                                     UPDATE_TIME | UPDATE_OPEN_COUNT |
                                     (local_testflag &
                                      T_STATISTICS ? UPDATE_STAT : 0));
    /* File is repaired; Mark the file as moved to this system */
    (void) _ma_set_uuid(share, 0);

    info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
         HA_STATUS_CONST);
    if (rows != file->state->records && !(param->testflag & T_VERY_SILENT))
    {
      char llbuff[22], llbuff2[22];
      _ma_check_print_warning(param, "Number of rows changed from %s to %s",
                              llstr(rows, llbuff),
                              llstr(file->state->records, llbuff2));
    }
  }
  else
  {
    maria_mark_crashed_on_repair(file);
    file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
    maria_update_state_info(param, file, 0);
  }
  mysql_mutex_unlock(&share->intern_lock);
  thd_proc_info(thd, old_proc_info);
  thd_progress_end(thd);                        // Mark done
  if (locking)
    maria_lock_database(file, F_UNLCK);

  /* Reset trn, that may have been set by repair */
  if (old_trn && old_trn != file->trn)
    _ma_set_trn_for_table(file, old_trn);
  error= error ? HA_ADMIN_FAILED :
    (optimize_done ?
     (write_log_record_for_repair(param, file) ? HA_ADMIN_FAILED :
      HA_ADMIN_OK) : HA_ADMIN_ALREADY_DONE);
  DBUG_RETURN(error);
}


int maria_checkpoint_state(handlerton *hton, bool disabled)
{
  maria_checkpoint_disabled= (my_bool) disabled;
  return 0;
}


bool maria_flush_logs(handlerton *hton)
{
  return MY_TEST(translog_purge_at_flush());
}


int ha_maria::open(const char *name, int mode, uint test_if_locked)
{
  uint i;

#ifdef NOT_USED
  /*
    If the user wants to have memory mapped data files, add an
    open_flag. Do not memory map temporary tables because they are
    expected to be inserted and thus extended a lot. Memory mapping is
    efficient for files that keep their size, but very inefficient for
    growing files. Using an open_flag instead of calling ma_extra(...
    HA_EXTRA_MMAP ...) after maxs_open() has the advantage that the
    mapping is not repeated for every open, but just done on the initial
    open, when the MyISAM share is created. Every time the server
    requires to open a new instance of a table it calls this method. We
    will always supply HA_OPEN_MMAP for a permanent table. However, the
    Maria storage engine will ignore this flag if this is a secondary
    open of a table that is in use by other threads already (if the
    Maria share exists already).
  */
  if (!(test_if_locked & HA_OPEN_TMP_TABLE) && opt_maria_use_mmap)
    test_if_locked|= HA_OPEN_MMAP;
#endif

  if (maria_recover_options & HA_RECOVER_ANY)
  {
    /* user asked to trigger a repair if table was not properly closed */
    test_if_locked|= HA_OPEN_ABORT_IF_CRASHED;
  }

  if (aria_readonly)
    test_if_locked|= HA_OPEN_IGNORE_MOVED_STATE;

  if (!(file= maria_open(name, mode, test_if_locked | HA_OPEN_FROM_SQL_LAYER,
                         s3_open_args())))
  {
    if (my_errno == HA_ERR_OLD_FILE)
    {
      push_warning(current_thd, Sql_condition::WARN_LEVEL_NOTE,
                   ER_CRASHED_ON_USAGE,
                   zerofill_error_msg);
    }
    return (my_errno ? my_errno : -1);
  }
  if (aria_readonly)
    file->s->options|= HA_OPTION_READ_ONLY_DATA;

  file->s->chst_invalidator= query_cache_invalidate_by_MyISAM_filename_ref;
  /* Set external_ref, mainly for temporary tables */
  file->external_ref= (void*) table;            // For ma_killed()

  if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE))
    maria_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0);

  info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
  if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
    maria_extra(file, HA_EXTRA_WAIT_LOCK, 0);
  if ((data_file_type= file->s->data_file_type) != STATIC_RECORD)
    int_table_flags |= HA_REC_NOT_IN_SEQ;
  if (!file->s->base.born_transactional)
  {
    /*
      INSERT DELAYED cannot work with transactional tables (because it cannot
      stand up to "when client gets ok the data is safe on disk": the record
      may not even be inserted). In the future, we could enable it back (as a
      client doing INSERT DELAYED knows the specificities; but we then should
      make sure to regularly commit in the delayed_insert thread).
    */
    int_table_flags|= HA_CAN_INSERT_DELAYED | HA_NO_TRANSACTIONS;
  }
  else
    int_table_flags|= HA_CRASH_SAFE;

  if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
    int_table_flags |= HA_HAS_NEW_CHECKSUM;

  /*
    We can only do online backup on transactional tables with checksum.
    Checksums are needed to avoid half writes.
  */
  if (file->s->options & HA_OPTION_PAGE_CHECKSUM &&
      file->s->base.born_transactional)
    int_table_flags |= HA_CAN_ONLINE_BACKUPS;

  /*
    For static size rows, tell MariaDB that we will access all bytes
    in the record when writing it.  This signals MariaDB to initialize
    the full row to ensure we don't get any errors from valgrind and
    that all bytes in the row is properly reset.
  */
  if (file->s->data_file_type == STATIC_RECORD &&
      (file->s->has_varchar_fields || file->s->has_null_fields))
    int_table_flags|= HA_RECORD_MUST_BE_CLEAN_ON_WRITE;

  for (i= 0; i < table->s->keys; i++)
  {
    plugin_ref parser= table->key_info[i].parser;
    if (table->key_info[i].flags & HA_USES_PARSER)
      file->s->keyinfo[i].parser=
        (struct st_mysql_ftparser *)plugin_decl(parser)->info;
    table->key_info[i].block_size= file->s->keyinfo[i].block_length;
  }
  my_errno= 0;

  /* Count statistics of usage for newly open normal files */
  if (file->s->reopen == 1 && ! (test_if_locked & HA_OPEN_TMP_TABLE))
  {
    if (file->s->delay_key_write)
      feature_files_opened_with_delayed_keys++;
  }

  return my_errno;
}


int ha_maria::index_next(uchar * buf)
{
  DBUG_ASSERT(inited == INDEX);
  register_handler(file);
  int error= maria_rnext(file, buf, active_index);
  return error;
}


int ha_maria::close(void)
{
  MARIA_HA *tmp= file;
  if (!tmp)
    return 0;
  DBUG_ASSERT(file->trn == 0 || file->trn == &dummy_transaction_object);
  DBUG_ASSERT(file->trn_next == 0 && file->trn_prev == 0);
  file= 0;
  return maria_close(tmp);
}


void maria_end_backup()
{
  translog_enable_purge();
}


static void update_log_file_size(MYSQL_THD thd,
                                 struct st_mysql_sys_var *var,
                                 void *var_ptr, const void *save)
{
  uint32 size= (uint32)((ulong)(*(long *)save));
  translog_set_file_size(size);
  *(ulong *)var_ptr= size;
}


int ha_maria::start_stmt(THD *thd, thr_lock_type lock_type)
{
  TRN *trn;
  if (file->s->base.born_transactional)
  {
    trn= THD_TRN;
    DBUG_ASSERT(trn); // this may be called only after external_lock()
    DBUG_ASSERT(trnman_has_locked_tables(trn));
    DBUG_ASSERT(lock_type != TL_UNLOCK);
    DBUG_ASSERT(file->trn == trn);

    /*
      As external_lock() was already called, don't increment locked_tables.
      Note that we call the function below possibly several times when
      statement starts (once per table). This is ok as long as that function
      does cheap operations. Otherwise, we will need to do it only on first
      call to start_stmt().
    */
    trnman_new_statement(trn);

#ifdef EXTRA_DEBUG
    if (!(trnman_get_flags(trn) & TRN_STATE_INFO_LOGGED) &&
        trnman_get_flags(trn) & TRN_STATE_TABLES_CAN_CHANGE)
    {
      trnman_set_flags(trn, trnman_get_flags(trn) | TRN_STATE_INFO_LOGGED);
      (void) translog_log_debug_info(trn, LOGREC_DEBUG_INFO_QUERY,
                                     (uchar*) thd->query(),
                                     thd->query_length());
    }
#endif
  }
  return 0;
}


int ha_maria::index_read_map(uchar * buf, const uchar * key,
			     key_part_map keypart_map,
			     enum ha_rkey_function find_flag)
{
  DBUG_ASSERT(inited == INDEX);
  register_handler(file);
  int error= maria_rkey(file, buf, active_index, key, keypart_map, find_flag);
  return error;
}


int ha_maria::find_unique_row(uchar *record, uint constrain_no)
{
  int rc;
  register_handler(file);
  if (file->s->state.header.uniques)
  {
    DBUG_ASSERT(file->s->state.header.uniques > constrain_no);
    MARIA_UNIQUEDEF *def= file->s->uniqueinfo + constrain_no;
    ha_checksum unique_hash= _ma_unique_hash(def, record);
    rc= _ma_check_unique(file, def, record, unique_hash, HA_OFFSET_ERROR);
    if (rc)
    {
      file->cur_row.lastpos= file->dup_key_pos;
      if ((*file->read_record)(file, record, file->cur_row.lastpos))
        return -1;
      file->update|= HA_STATE_AKTIV;                     /* Record is read */
    }
    // invert logic
    rc= !MY_TEST(rc);
  }
  else
  {
    /*
     It is case when just unique index used instead unicue constrain
     (conversion from heap table).
     */
    DBUG_ASSERT(file->s->state.header.keys > constrain_no);
    MARIA_KEY key;
    file->once_flags|= USE_PACKED_KEYS;
    (*file->s->keyinfo[constrain_no].make_key)
      (file, &key, constrain_no, file->lastkey_buff2, record, 0, 0);
    rc= maria_rkey(file, record, constrain_no, key.data, key.data_length,
                   HA_READ_KEY_EXACT);
    rc= MY_TEST(rc);
  }
  return rc;
}


Item *ha_maria::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
{
  /*
    Check if the key contains a blob field. If it does then MyISAM
    should not accept the pushed index condition since MyISAM will not
    read the blob field from the index entry during evaluation of the
    pushed index condition and the BLOB field might be part of the
    range evaluation done by the ICP code.
  */
  const KEY *key= &table_share->key_info[keyno_arg];

  for (uint k= 0; k < key->user_defined_key_parts; ++k)
  {
    const KEY_PART_INFO *key_part= &key->key_part[k];
    if (key_part->key_part_flag & HA_BLOB_PART)
    {
      /* Let the server handle the index condition */
      return idx_cond_arg;
    }
  }

  pushed_idx_cond_keyno= keyno_arg;
  pushed_idx_cond= idx_cond_arg;
  in_range_check_pushed_down= TRUE;
  if (active_index == pushed_idx_cond_keyno)
    ma_set_index_cond_func(file, handler_index_cond_check, this);
  return NULL;
}


ha_rows ha_maria::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
                                               void *seq_init_param,
                                               uint n_ranges, uint *bufsz,
                                               uint *flags, Cost_estimate *cost)
{
  /*
    This call is here because there is no location where this->table would
    already be known.
    TODO: consider moving it into some per-query initialization call.
  */
  ds_mrr.init(this, table);
  return ds_mrr.dsmrr_info_const(keyno, seq, seq_init_param, n_ranges, bufsz,
                                 flags, cost);
}


bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
{
  TABLE *table;
  const char *key;
  uint	key_length;
  const char *alias= table_list->alias.str;
  uint flags= ot_ctx->get_flags();
  MDL_ticket *mdl_ticket;
  TABLE_SHARE *share;
  uint gts_flags;
  bool from_share= false;
#ifdef WITH_PARTITION_STORAGE_ENGINE
  int part_names_error=0;
#endif
  DBUG_ENTER("open_table");

  /*
    The table must not be opened already. The table can be pre-opened for
    some statements if it is a temporary table.

    open_temporary_table() must be used to open temporary tables.
  */
  DBUG_ASSERT(!table_list->table);

  /* an open table operation needs a lot of the stack space */
  if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
    DBUG_RETURN(TRUE);

  if (!(flags & MYSQL_OPEN_IGNORE_KILLED) && thd->killed)
  {
    thd->send_kill_message();
    DBUG_RETURN(TRUE);
  }

  /*
    Check if we're trying to take a write lock in a read only transaction.

    Note that we allow write locks on log tables as otherwise logging
    to general/slow log would be disabled in read only transactions.
  */
  if (table_list->mdl_request.is_write_lock_request() &&
      thd->tx_read_only &&
      !(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK)))
  {
    my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
    DBUG_RETURN(true);
  }

  if (!table_list->db.str)
  {
    my_error(ER_NO_DB_ERROR, MYF(0));
    DBUG_RETURN(true);
  }

  key_length= get_table_def_key(table_list, &key);

  /*
    If we're in pre-locked or LOCK TABLES mode, let's try to find the
    requested table in the list of pre-opened and locked tables. If the
    table is not there, return an error - we can't open not pre-opened
    tables in pre-locked/LOCK TABLES mode.
    TODO: move this block into a separate function.
  */
  if (thd->locked_tables_mode &&
      ! (flags & MYSQL_OPEN_GET_NEW_TABLE))
  {						// Using table locks
    TABLE *best_table= 0;
    int best_distance= INT_MIN;
    for (table=thd->open_tables; table ; table=table->next)
    {
      if (table->s->table_cache_key.length == key_length &&
	  !memcmp(table->s->table_cache_key.str, key, key_length))
      {
        if (!my_strcasecmp(system_charset_info, table->alias.c_ptr(), alias) &&
            table->query_id != thd->query_id && /* skip tables already used */
            (thd->locked_tables_mode == LTM_LOCK_TABLES ||
             table->query_id == 0))
        {
          int distance= ((int) table->reginfo.lock_type -
                         (int) table_list->lock_type);

          /*
            Find a table that either has the exact lock type requested,
            or has the best suitable lock. In case there is no locked
            table that has an equal or higher lock than requested,
            we us the closest matching lock to be able to produce an error
            message about wrong lock mode on the table. The best_table
            is changed if bd < 0 <= d or bd < d < 0 or 0 <= d < bd.

            distance <  0 - No suitable lock found
            distance >  0 - we have lock mode higher then we require
            distance == 0 - we have lock mode exactly which we need
          */
          if ((best_distance < 0 && distance > best_distance) ||
              (distance >= 0 && distance < best_distance))
          {
            best_distance= distance;
            best_table= table;
            if (best_distance == 0)
            {
              /*
                We have found a perfect match and can finish iterating
                through open tables list. Check for table use conflict
                between calling statement and SP/trigger is done in
                lock_tables().
              */
              break;
            }
          }
        }
      }
    }
    if (best_table)
    {
      table= best_table;
      table->query_id= thd->query_id;
      table->init(thd, table_list);
      DBUG_PRINT("info",("Using locked table"));
#ifdef WITH_PARTITION_STORAGE_ENGINE
      part_names_error= set_partitions_as_used(table_list, table);
#endif
      goto reset;
    }

    if (is_locked_view(thd, table_list))
    {
      if (table_list->sequence)
      {
        my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, table_list->alias.str);
        DBUG_RETURN(true);
      }
      DBUG_RETURN(FALSE); // VIEW
    }

    /*
      No table in the locked tables list. In case of explicit LOCK TABLES
      this can happen if a user did not include the table into the list.
      In case of pre-locked mode locked tables list is generated automatically,
      so we may only end up here if the table did not exist when
      locked tables list was created.
    */
    if (thd->locked_tables_mode == LTM_PRELOCKED)
      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str, table_list->alias.str);
    else
      my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias);
    DBUG_RETURN(TRUE);
  }

  /*
    Non pre-locked/LOCK TABLES mode, and the table is not temporary.
    This is the normal use case.
  */

  if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
  {
    /*
      We are not under LOCK TABLES and going to acquire write-lock/
      modify the base table. We need to acquire protection against
      global read lock until end of this statement in order to have
      this statement blocked by active FLUSH TABLES WITH READ LOCK.

      We don't need to acquire this protection under LOCK TABLES as
      such protection already acquired at LOCK TABLES time and
      not released until UNLOCK TABLES.

      We don't block statements which modify only temporary tables
      as these tables are not preserved by any form of
      backup which uses FLUSH TABLES WITH READ LOCK.

      TODO: The fact that we sometimes acquire protection against
            GRL only when we encounter table to be write-locked
            slightly increases probability of deadlock.
            This problem will be solved once Alik pushes his
            temporary table refactoring patch and we can start
            pre-acquiring metadata locks at the beggining of
            open_tables() call.
    */
    if (table_list->mdl_request.is_write_lock_request() &&
        ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
                    MYSQL_OPEN_FORCE_SHARED_MDL |
                    MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
                    MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
        ! ot_ctx->has_protection_against_grl())
    {
      MDL_request protection_request;
      MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);

      if (thd->global_read_lock.can_acquire_protection())
        DBUG_RETURN(TRUE);

      protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
                              MDL_STATEMENT);

      /*
        Install error handler which if possible will convert deadlock error
        into request to back-off and restart process of opening tables.
      */
      thd->push_internal_handler(&mdl_deadlock_handler);
      bool result= thd->mdl_context.acquire_lock(&protection_request,
                                                 ot_ctx->get_timeout());
      thd->pop_internal_handler();

      if (result)
        DBUG_RETURN(TRUE);

      ot_ctx->set_has_protection_against_grl();
    }

    if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
                                flags, &mdl_ticket) ||
        mdl_ticket == NULL)
    {
      DEBUG_SYNC(thd, "before_open_table_wait_refresh");
      DBUG_RETURN(TRUE);
    }
    DEBUG_SYNC(thd, "after_open_table_mdl_shared");
  }
  else
  {
    /*
      Grab reference to the MDL lock ticket that was acquired
      by the caller.
    */
    mdl_ticket= table_list->mdl_request.ticket;
  }

  if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
  {
    if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
      DBUG_RETURN(FALSE);
  }
  else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
    DBUG_RETURN(FALSE);

  /* Table exists. Let us try to open it. */

  if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
    gts_flags= GTS_TABLE;
  else if (table_list->i_s_requested_object &  OPEN_VIEW_ONLY)
    gts_flags= GTS_VIEW;
  else
    gts_flags= GTS_TABLE | GTS_VIEW;

retry_share:

  share= tdc_acquire_share(thd, table_list, gts_flags, &table);

  if (unlikely(!share))
  {
    /*
      Hide "Table doesn't exist" errors if the table belongs to a view.
      The check for thd->is_error() is necessary to not push an
      unwanted error in case the error was already silenced.
      @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
    */
    if (thd->is_error())
    {
      if (table_list->parent_l)
      {
        thd->clear_error();
        my_error(ER_WRONG_MRG_TABLE, MYF(0));
      }
      else if (table_list->belong_to_view)
      {
        TABLE_LIST *view= table_list->belong_to_view;
        thd->clear_error();
        my_error(ER_VIEW_INVALID, MYF(0),
                 view->view_db.str, view->view_name.str);
      }
    }
    DBUG_RETURN(TRUE);
  }

  /*
    Check if this TABLE_SHARE-object corresponds to a view. Note, that there is
    no need to check TABLE_SHARE::tdc.flushed as we do for regular tables,
    because view shares are always up to date.
  */
  if (share->is_view)
  {
    /*
      If parent_l of the table_list is non null then a merge table
      has this view as child table, which is not supported.
    */
    if (table_list->parent_l)
    {
      my_error(ER_WRONG_MRG_TABLE, MYF(0));
      goto err_lock;
    }
    if (table_list->sequence)
    {
      my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str,
               table_list->alias.str);
      goto err_lock;
    }
    /*
      This table is a view. Validate its metadata version: in particular,
      that it was a view when the statement was prepared.
    */
    if (check_and_update_table_version(thd, table_list, share))
      goto err_lock;

    /* Open view */
    if (mysql_make_view(thd, share, table_list, false))
      goto err_lock;

    /* TODO: Don't free this */
    tdc_release_share(share);

    DBUG_ASSERT(table_list->view);

    DBUG_RETURN(FALSE);
  }

#ifdef WITH_WSREP
  if (!((flags & MYSQL_OPEN_IGNORE_FLUSH) ||
        (thd->wsrep_applier)))
#else
  if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
#endif
  {
    if (share->tdc->flushed)
    {
      DBUG_PRINT("info", ("Found old share version: %lld  current: %lld",
                          share->tdc->version, tdc_refresh_version()));
      /*
        We already have an MDL lock. But we have encountered an old
        version of table in the table definition cache which is possible
        when someone changes the table version directly in the cache
        without acquiring a metadata lock (e.g. this can happen during
        "rolling" FLUSH TABLE(S)).
        Release our reference to share, wait until old version of
        share goes away and then try to get new version of table share.
      */
      if (table)
        tc_release_table(table);
      else
        tdc_release_share(share);

      MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
      bool wait_result;

      thd->push_internal_handler(&mdl_deadlock_handler);
      wait_result= tdc_wait_for_old_version(thd, table_list->db.str,
                                            table_list->table_name.str,
                                            ot_ctx->get_timeout(),
                                            mdl_ticket->get_deadlock_weight());
      thd->pop_internal_handler();

      if (wait_result)
        DBUG_RETURN(TRUE);

      goto retry_share;
    }

    if (thd->open_tables && thd->open_tables->s->tdc->flushed)
    {
      /*
        If the version changes while we're opening the tables,
        we have to back off, close all the tables opened-so-far,
        and try to reopen them. Note: refresh_version is currently
        changed only during FLUSH TABLES.
      */
      if (table)
        tc_release_table(table);
      else
        tdc_release_share(share);
      (void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
                                           NULL);
      DBUG_RETURN(TRUE);
    }
  }

  if (table)
  {
    DBUG_ASSERT(table->file != NULL);
    MYSQL_REBIND_TABLE(table->file);
#ifdef WITH_PARTITION_STORAGE_ENGINE
    part_names_error= set_partitions_as_used(table_list, table);
#endif
  }
  else
  {
    enum open_frm_error error;

    /* make a new table */
    if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
      goto err_lock;

    error= open_table_from_share(thd, share, &table_list->alias,
                                 HA_OPEN_KEYFILE | HA_TRY_READ_ONLY,
                                 EXTRA_RECORD,
                                 thd->open_options, table, FALSE,
                                 IF_PARTITIONING(table_list->partition_names,0));

    if (unlikely(error))
    {
      my_free(table);

      if (error == OPEN_FRM_DISCOVER)
        (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
                                              table_list);
      else if (share->crashed)
      {
        if (!(flags & MYSQL_OPEN_IGNORE_REPAIR))
          (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
                                                table_list);
        else
          table_list->crashed= 1;  /* Mark that table was crashed */
      }
      goto err_lock;
    }
    if (open_table_entry_fini(thd, share, table))
    {
      closefrm(table);
      my_free(table);
      goto err_lock;
    }

    /* Add table to the share's used tables list. */
    tc_add_table(thd, table);
    from_share= true;
  }

  table->mdl_ticket= mdl_ticket;
  table->reginfo.lock_type=TL_READ;		/* Assume read */

  table->init(thd, table_list);

  table->next= thd->open_tables;		/* Link into simple list */
  thd->set_open_tables(table);

 reset:
  /*
    Check that there is no reference to a condition from an earlier query
    (cf. Bug#58553).
  */
  DBUG_ASSERT(table->file->pushed_cond == NULL);
  table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
  table_list->table= table;

  if (!from_share && table->vcol_fix_expr(thd))
    DBUG_RETURN(true);

#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (unlikely(table->part_info))
  {
    /* Partitions specified were incorrect.*/
    if (part_names_error)
    {
      table->file->print_error(part_names_error, MYF(0));
      DBUG_RETURN(true);
    }
  }
  else if (table_list->partition_names)
  {
    /* Don't allow PARTITION () clause on a nonpartitioned table */
    my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
    DBUG_RETURN(true);
  }
#endif
  if (table_list->sequence && table->s->table_type != TABLE_TYPE_SEQUENCE)
  {
    my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db.str, table_list->alias.str);
    DBUG_RETURN(true);
  }

  DBUG_RETURN(FALSE);

err_lock:
  tdc_release_share(share);

  DBUG_PRINT("exit", ("failed"));
  DBUG_RETURN(TRUE);
}

MDEV-24176 fixup: GCC -Wmaybe-uninitialized


void Item_equal::sort(Item_field_cmpfunc compare, void *arg)
{
  bubble_sort<Item>(&equal_items, compare, arg);
}

MDEV-26402: A SEGV in Item_field::used_tables/update_depend_map_for_order...

When doing condition pushdown from HAVING into WHERE,
Item_equal::create_pushable_equalities() calls
item->set_extraction_flag(IMMUTABLE_FL) for constant items.
Then, Item::cleanup_excluding_immutables_processor() checks for this flag
to see if it should call item->cleanup() or leave the item as-is.

The failure happens when a constant item has a non-constant one inside it,
like:

   (tbl.col=0 AND impossible_cond)

item->walk(cleanup_excluding_immutables_processor) works in a bottom-up
way so it
1. will call Item_func_eq(tbl.col=0)->cleanup()
2. will not call Item_cond_and->cleanup (as the AND is constant)

This creates an item tree where a fixed Item has an un-fixed Item inside
it which eventually causes an assertion failure.

Fixed by introducing this rule: instead of just calling

  item->set_extraction_flag(IMMUTABLE_FL);

we call Item::walk() to set the flag for all sub-items of the item.


  void join_with_sum_func(const Item *item)
  {
    m_with_sum_func|= item->with_sum_func();
  }


  Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
                             const Tmp_field_param *param)
  {
    return create_tmp_field_ex_simple(table, src, param);
  }


Item_func_nullif::time_op(THD *thd, MYSQL_TIME *ltime)
{
  DBUG_ASSERT(fixed == 1);
  if (!compare())
    return (null_value= true);
  return (null_value= Time(thd, args[2]).copy_to_mysql_time(ltime));

}


Item *Item_bool_rowready_func2::negated_item(THD *thd)
{
  DBUG_ASSERT(0);
  return 0;
}


  Item_cache_str(THD *thd, const Item *item):
    Item_cache(thd, item->type_handler()), value(0),
    is_varbinary(item->type() == FIELD_ITEM &&
                 Item_cache_str::field_type() == MYSQL_TYPE_VARCHAR &&
                 !((const Item_field *) item)->field->has_charset())
  {
    collation.set(const_cast<DTCollation&>(item->collation));
  }


  Item_cache_datetime(THD *thd)
   :Item_cache_temporal(thd, &type_handler_datetime2) { }


  Item_fixed_hybrid(THD *thd, Item_fixed_hybrid *item)
   :Item(thd, item), fixed(item->fixed)
  { }


Item_func_nullif::real_op()
{
  DBUG_ASSERT(fixed == 1);
  double value;
  if (!compare())
  {
    null_value=1;
    return 0.0;
  }
  value= args[2]->val_real();
  null_value= args[2]->null_value;
  return value;
}


void Item_func_nullif::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
                                      List<Item> &fields, uint flags)
{
  if (m_cache)
  {
    flags|= SPLIT_SUM_SKIP_REGISTERED; // See Item_func::split_sum_func
    m_cache->split_sum_func2_example(thd, ref_pointer_array, fields, flags);
    args[1]->split_sum_func2(thd, ref_pointer_array, fields, &args[1], flags);
  }
  else
  {
    Item_func::split_sum_func(thd, ref_pointer_array, fields, flags);
  }
}


virtual const String *const_ptr_string() const { return NULL; }


  bool agg_arg_charsets_for_string_result(DTCollation &c,
                                          Item **items, uint nitems,
                                          int item_sep= 1)
  {
    return Type_std_attributes::
      agg_arg_charsets_for_string_result(c, func_name(),
                                         items, nitems, item_sep);
  }


bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp)
{
  DBUG_ASSERT(arg_count >= 2); // Item_func_nullif has arg_count == 3

  if (args[0]->cmp_type() == STRING_RESULT &&
      args[1]->cmp_type() == STRING_RESULT)
  {
    DTCollation tmp;
    if (agg_arg_charsets_for_comparison(tmp, args, 2))
      return true;
    cmp->m_compare_collation= tmp.collation;
  }
  //  Convert constants when compared to int/year field
  DBUG_ASSERT(functype() != LIKE_FUNC);
  convert_const_compared_to_int_field(thd);

  return cmp->set_cmp_func(this, &args[0], &args[1], true);
}


  Item_time_literal(THD *thd, const Time *ltime, uint dec_arg):
    Item_temporal_literal(thd, dec_arg),
    cached_time(*ltime)
  {
    DBUG_ASSERT(cached_time.is_valid_time());
    max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0);
  }


bool Arg_comparator::set_cmp_func_real()
{
  if ((((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() &&
        (*b)->result_type() == STRING_RESULT  &&  (*b)->const_item()) ||
      ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() &&
       (*a)->result_type() == STRING_RESULT  &&  (*a)->const_item())))
  {
    /*
     <non-const decimal expression> <cmp> <const string expression>
     or
     <const string expression> <cmp> <non-const decimal expression>

     Do comparison as decimal rather than float, in order not to lose precision.
    */
    m_compare_handler= &type_handler_newdecimal;
    return set_cmp_func_decimal();
  }

  THD *thd= current_thd;
  func= is_owner_equal_func() ? &Arg_comparator::compare_e_real :
                                &Arg_comparator::compare_real;
  if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC)
  {
    precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1];
    if (func == &Arg_comparator::compare_real)
      func= &Arg_comparator::compare_real_fixed;
    else if (func == &Arg_comparator::compare_e_real)
      func= &Arg_comparator::compare_e_real_fixed;
  }
  a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
  b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
  return false;
}


  longlong val_time_packed(THD *thd)
  {
    return has_value() ? value : 0;
  }


longlong Item_func_between::val_int_cmp_string()
{
  String *value,*a,*b;
  value=args[0]->val_str(&value0);
  if ((null_value=args[0]->null_value))
    return 0;
  a= args[1]->val_str(&value1);
  b= args[2]->val_str(&value2);
  if (!args[1]->null_value && !args[2]->null_value)
    return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 &&
                        sortcmp(value,b,cmp_collation.collation) <= 0) !=
                       negated);
  if (args[1]->null_value && args[2]->null_value)
    null_value= true;
  else if (args[1]->null_value)
  {
    // Set to not null if false range.
    null_value= sortcmp(value,b,cmp_collation.collation) <= 0;
  }
  else
  {
    // Set to not null if false range.
    null_value= sortcmp(value,a,cmp_collation.collation) >= 0;
  }
  return (longlong) (!null_value && negated);
}


  virtual bool cleanup_excluding_fields_processor (void *arg)
  { return cleanup_processor(arg); }


  double val_real()
  {
    return m_value.to_datetime(current_thd).to_double();
  }


Item_func_nullif::decimal_op(my_decimal * decimal_value)
{
  DBUG_ASSERT(fixed == 1);
  my_decimal *res;
  if (!compare())
  {
    null_value=1;
    return 0;
  }
  res= args[2]->val_decimal(decimal_value);
  null_value= args[2]->null_value;
  return res;
}


  const String *const_ptr_string() const
  { return can_return_const_value(STRING_RESULT) ? &value.m_string : NULL; }


  With_sum_func_cache(const Item *a, const Item *b)
   :m_with_sum_func(a->with_sum_func() || b->with_sum_func())
  { }


  Name_resolution_context_backup(Name_resolution_context &_ctx, TABLE_LIST &_table_list)
    : ctx(_ctx), table_list(_table_list), save_map(_table_list.map)
  {
    ctx_state.save_state(&ctx, &table_list);
    ctx.table_list= &table_list;
    ctx.first_name_resolution_table= &table_list;
  }


Item *Item_bool_rowready_func2::neg_transformer(THD *thd)
{
  Item *item= negated_item(thd);
  return item;
}


const Item_const *get_item_const() const { return this; }


  Item *get_copy(THD *thd)
  { return get_item_copy<Item_direct_ref>(thd, this); }


  double val_real()
  {
    return update_null() ? 0 : cached_time.to_double();
  }


cmp_item* cmp_item_real::make_same()
{
  return new cmp_item_real();
}


Item_func_nullif::date_op(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
  DBUG_ASSERT(fixed == 1);
  if (!compare())
    return (null_value= true);
  Datetime_truncation_not_needed dt(thd, args[2], fuzzydate);
  return (null_value= dt.copy_to_mysql_time(ltime, mysql_timestamp_type()));
}


bool Item_func_regex::fix_fields(THD *thd, Item **ref)
{
  re.set_recursion_limit(thd);
  return Item_bool_func::fix_fields(thd, ref);
}


int Arg_comparator::compare_e_str_json()
{
  return compare_e_json_str_basic(*b, *a);
}


Item_bool_rowready_func2* Ne_creator::create(THD *thd, Item *a, Item *b) const
{
  return new(thd->mem_root) Item_func_ne(thd, a, b);
}


bool Item_func_interval::fix_fields(THD *thd, Item **ref)
{
  if (Item_long_func::fix_fields(thd, ref))
    return true;
  for (uint i= 0 ; i < row->cols(); i++)
  {
    if (row->element_index(i)->check_cols(1))
      return true;
  }
  return false;
}


  Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
                             const Tmp_field_param *param)
  {
    DBUG_ASSERT(0);
    return NULL;
  }


  Item_literal(THD *thd): Item_basic_constant(thd)
  { }


longlong Item_func_dyncol_check::val_int()
{
  char buff[STRING_BUFFER_USUAL_SIZE];
  String tmp(buff, sizeof(buff), &my_charset_bin);
  DYNAMIC_COLUMN col;
  String *str;
  enum enum_dyncol_func_result rc;

  str= args[0]->val_str(&tmp);
  if (args[0]->null_value)
    goto null;
  col.length= str->length();
  /* We do not change the string, so could do this trick */
  col.str= (char *)str->ptr();
  rc= mariadb_dyncol_check(&col);
  if (rc < 0 && rc != ER_DYNCOL_FORMAT)
  {
    dynamic_column_error_message(rc);
    goto null;
  }
  null_value= FALSE;
  return rc == ER_DYNCOL_OK;

null:
  null_value= TRUE;
  return 0;
}


  bool agg_arg_charsets_for_comparison(DTCollation &c,
                                       Item **items, uint nitems,
                                       int item_sep= 1)
  {
    return Type_std_attributes::
      agg_arg_charsets_for_comparison(c, func_name(), items, nitems, item_sep);
  }


  Longlong_null to_longlong_null()
  {
    longlong nr= val_int();
    /*
      C++ does not guarantee the order of parameter evaluation,
      so to make sure "null_value" is passed to the constructor
      after the val_int() call, val_int() is caled on a separate line.
    */
    return Longlong_null(nr, null_value);
  }


bool Regexp_processor_pcre::compile(Item *item, bool send_error)
{
  char buff[MAX_FIELD_WIDTH];
  String tmp(buff, sizeof(buff), &my_charset_bin);
  String *pattern= item->val_str(&tmp);
  if (unlikely(item->null_value) || (unlikely(compile(pattern, send_error))))
    return true;
  return false;
}


  bool check_cols(uint c)
  {
    if (cols() != c)
    {
      my_error(ER_OPERAND_COLUMNS, MYF(0), c);
      return true;
    }
    return false;
  }


  bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
  { return get_date_from_real(thd, ltime, fuzzydate); }


bool Item_equal::merge_with_check(THD *thd, Item_equal *item, bool save_merged)
{
  bool intersected= FALSE;
  Item_equal_fields_iterator_slow fi(*item);

  while (fi++)
  {
    if (contains(fi.get_curr_field()))
    {
      intersected= TRUE;
      if (!save_merged)
        fi.remove();
    }
  }
  if (intersected)
  {
    if (!save_merged)
      merge(thd, item);
    else
    {
      Item *c= item->get_const();
      if (c)
        add_const(thd, c);
      if (!cond_false)
      {
        Item *item;
        fi.rewind();
        while ((item= fi++))
	{
          if (!contains(fi.get_curr_field()))
            add(item, thd->mem_root);
        }
      }
    }
  }
  return intersected;
}


  Item_int(THD *thd, const char *str_arg,longlong i,size_t length, bool flag):
    Item_num(thd), value(i)
    {
      max_length=(uint32)length;
      name.str= str_arg; name.length= safe_strlen(name.str);
      unsigned_flag= flag;
    }


bool is_order_clause_position() const { return true; }


  my_decimal *val_decimal(my_decimal *decimal_value)
  {
    longlong value= Item_hex_hybrid::val_int();
    int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value);
    return decimal_value;
  }


  my_decimal *val_decimal(my_decimal *to)
  {
    return has_value() ? Date(this).to_decimal(to) : NULL;
  }


  uint cols() const
  {
    return ref && result_type() == ROW_RESULT ? (*ref)->cols() : 1;
  }


bool Item_func_regexp_instr::fix_fields(THD *thd, Item **ref)
{
  re.set_recursion_limit(thd);
  return Item_int_func::fix_fields(thd, ref);
}


  Item_cache_int(THD *thd, const Type_handler *handler):
    Item_cache(thd, handler), value(0) {}


Item_func_in::fix_fields(THD *thd, Item **ref)
{

  if (Item_func_opt_neg::fix_fields(thd, ref))
    return 1;

  return 0;
}


  Item_temporal_literal(THD *thd)
   :Item_literal(thd)
  {
    collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
    decimals= 0;
  }


Item_func_if::eval_not_null_tables(void *opt_arg)
{
  if (Item_func::eval_not_null_tables(NULL))
    return 1;

  not_null_tables_cache= (args[1]->not_null_tables() &
                          args[2]->not_null_tables());

  return 0;
}


bool const_is_null() const { return true; }


  bool val_native_with_conversion_result(THD *thd, Native *to,
                                         const Type_handler *th)
  {
    return th->Item_val_native_with_conversion_result(thd, this, to);
  }


  Item_blob(THD *thd, const char *name_arg, uint length):
    Item_partition_func_safe_string(thd, name_arg, (uint) safe_strlen(name_arg),
                                    &my_charset_bin)
  { max_length= length; }


  inline void append(const char *str, uint length)
  {
    str_value.append(str, length);
    max_length= str_value.numchars() * collation.collation->mbmaxlen;
  }


  longlong val_int()
  {
    return has_value() ? Datetime(this).to_longlong() : 0;
  }


int Arg_comparator::compare_int_unsigned_signed()
{
  ulonglong uval1= (ulonglong)(*a)->val_int();
  if (!(*a)->null_value)
  {
    longlong sval2= (*b)->val_int();
    if (!(*b)->null_value)
    {
      if (set_null)
        owner->null_value= 0;
      if (sval2 < 0)
        return 1;
      if (uval1 < (ulonglong)sval2)
        return -1;
      if (uval1 == (ulonglong)sval2)
        return 0;
      return 1;
    }
  }
  if (set_null)
    owner->null_value= 1;
  return -1;
}


  virtual void quick_fix_field()
  {
    DBUG_ASSERT(0);
  }


double val_real() { return decimal_value.to_double(); }


longlong Item_func_between::val_int_cmp_decimal()
{
  VDec dec(args[0]);
  if ((null_value= dec.is_null()))
    return 0;					/* purecov: inspected */
  VDec a_dec(args[1]), b_dec(args[2]);
  if (!a_dec.is_null() && !b_dec.is_null())
    return (longlong) ((dec.cmp(a_dec) >= 0 &&
                        dec.cmp(b_dec) <= 0) != negated);
  if (a_dec.is_null() && b_dec.is_null())
    null_value= true;
  else if (a_dec.is_null())
    null_value= (dec.cmp(b_dec) <= 0);
  else
    null_value= (dec.cmp(a_dec) >= 0);
  return (longlong) (!null_value && negated);
}


void Item_func_case_simple::print(String *str, enum_query_type query_type)
{
  Item **pos;
  str->append(STRING_WITH_LEN("case "));
  args[0]->print_parenthesised(str, query_type, precedence());
  str->append(' ');
  print_when_then_arguments(str, query_type, &args[1], when_count());
  if ((pos= Item_func_case_simple::else_expr_addr()))
    print_else_argument(str, query_type, pos[0]);
  str->append(STRING_WITH_LEN("end"));
}


  Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d)
  {
    arg_count= 0;
    if (likely((args= (Item**) thd_alloc(thd, sizeof(Item*) * 4))))
    {
      arg_count= 4;
      args[0]= a; args[1]= b; args[2]= c; args[3]= d;
    }
  }


  void set_geometry_type(uint type)
  { Type_geometry_attributes::set_geometry_type(type); }


longlong Item_func_le::val_int()
{
  DBUG_ASSERT(fixed == 1);
  int value= cmp.compare();
  return value <= 0 && !null_value ? 1 : 0;
}


  Item_date_literal(THD *thd, const Date *ltime)
    :Item_temporal_literal(thd),
     cached_time(*ltime)
  {
    DBUG_ASSERT(cached_time.is_valid_date());
    max_length= MAX_DATE_WIDTH;
    /*
      If date has zero month or day, it can return NULL in case of
      NO_ZERO_DATE or NO_ZERO_IN_DATE.
      If date is `February 30`, it can return NULL in case if
      no ALLOW_INVALID_DATES is set.
      We can't set null_value using the current sql_mode here in constructor,
      because sql_mode can change in case of prepared statements
      between PREPARE and EXECUTE.
      Here we only set maybe_null to true if the value has such anomalies.
      Later (during execution time), if maybe_null is true, then the value
      will be checked per row, according to the execution time sql_mode.
      The check_date() below call should cover all cases mentioned.
    */
    maybe_null= cached_time.check_date(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
  }


  bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
  {
    bool res= m_value.to_TIME(thd, ltime, fuzzydate);
    DBUG_ASSERT(!res);
    return res;
  }


  longlong val_int_from_real()
  {
    DBUG_ASSERT(is_fixed());
    return Converter_double_to_longlong_with_warn(val_real(), false).result();
  }


Item *Item_cond::build_clone(THD *thd)
{
  List_iterator_fast<Item> li(list);
  Item *item;
  Item_cond *copy= (Item_cond *) get_copy(thd);
  if (!copy)
    return 0;
  copy->list.empty();
  while ((item= li++))
  {
    Item *arg_clone= item->build_clone(thd);
    if (!arg_clone)
      return 0;
    if (copy->list.push_back(arg_clone, thd->mem_root))
      return 0;
  }
  return copy;
}


  const char *field_name_or_null()
  { return real_item()->type() == Item::FIELD_ITEM ? name.str : NULL; }


  longlong val_time_packed(THD *thd)
  {
    return Time(thd, this, Time::Options_cmp(thd)).to_packed();
  }


void Item_func_like::cleanup()
{
  canDoTurboBM= FALSE;
  Item_bool_func2::cleanup();
}


  bool fix_fields(THD *thd, Item **it)
  {
    DBUG_ASSERT(ident->type() == FIELD_ITEM || ident->type() == REF_ITEM);
    if (ident->fix_fields_if_needed_for_scalar(thd, ref))
      return TRUE;
    set_properties();
    return FALSE;
  }


bool Item_in_optimizer::is_top_level_item()
{
  if (!invisible_mode())
    return ((Item_in_subselect *)args[1])->is_top_level_item();
  return false;
}


bool Item_func_in::fix_for_scalar_comparison_using_cmp_items(THD *thd,
                                                             uint found_types)
{
  if (found_types & (1U << STRING_RESULT) &&
      agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
    return true;
  if (make_unique_cmp_items(thd, cmp_collation.collation))
    return true;
  return false;
}


  String *val_str(String *str)
  {
    return can_return_value() ? value.val_str(str, this) : NULL;
  }


  Item_insert_value(THD *thd, Name_resolution_context *context_arg, Item *a)
    :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL,
                &null_clex_str),
     arg(a) {}


Item *Item_func_case_searched::find_item()
{
  uint count= when_count();
  for (uint i= 0 ; i < count ; i++)
  {
    if (args[i]->val_bool())
      return args[i + count];
  }
  Item **pos= Item_func_case_searched::else_expr_addr();
  return pos ? pos[0] : 0;
}


bool Item_func_not_all::empty_underlying_subquery()
{
  return ((test_sum_item && !test_sum_item->any_value()) ||
          (test_sub_item && !test_sub_item->any_value()));
}


const String *const_ptr_string() const { return &str_value; }


  bool agg_arg_charsets_for_string_result_with_comparison(DTCollation &c,
                                                          Item **items,
                                                          uint nitems,
                                                          int item_sep= 1)
  {
    return Type_std_attributes::
      agg_arg_charsets_for_string_result_with_comparison(c, func_name(),
                                                         items, nitems,
                                                         item_sep);
  }


  enum_field_types field_type() const
  {
    return type_handler()->field_type();
  }


  longlong val_datetime_packed(THD *thd)
  {
    if (check_null_ref())
      return 0;
    else
      return Item_direct_ref::val_datetime_packed(thd);
  }


int Arg_comparator::compare_real_fixed()
{
  /*
    Fix yet another manifestation of Bug#2338. 'Volatile' will instruct
    gcc to flush double values out of 80-bit Intel FPU registers before
    performing the comparison.
  */
  volatile double val1, val2;
  val1= (*a)->val_real();
  if (!(*a)->null_value)
  {
    val2= (*b)->val_real();
    if (!(*b)->null_value)
    {
      if (set_null)
        owner->null_value= 0;
      if (val1 == val2 || fabs(val1 - val2) < precision)
        return 0;
      if (val1 < val2)
        return -1;
      return 1;
    }
  }
  if (set_null)
    owner->null_value= 1;
  return -1;
}


longlong Item_func_like::val_int()
{
  DBUG_ASSERT(fixed == 1);
  DBUG_ASSERT(escape != ESCAPE_NOT_INITIALIZED);
  String* res= args[0]->val_str(&cmp_value1);
  if (args[0]->null_value)
  {
    null_value=1;
    return 0;
  }
  String* res2= args[1]->val_str(&cmp_value2);
  if (args[1]->null_value)
  {
    null_value=1;
    return 0;
  }
  null_value=0;
  if (canDoTurboBM)
    return turboBM_matches(res->ptr(), res->length()) ? !negated : negated;
  return my_wildcmp(cmp_collation.collation,
		    res->ptr(),res->ptr()+res->length(),
		    res2->ptr(),res2->ptr()+res2->length(),
		    escape,wild_one,wild_many) ? negated : !negated;
}


  Item_ignore_specification(THD *thd)
   :Item_contextually_typed_value_specification(thd)
  { }


virtual const MYSQL_TIME *const_ptr_mysql_time() const { return NULL; }


double val_real() { return (double) value; }


  Item *get_copy(THD *thd)
  { return get_item_copy<Item_cache_datetime>(thd, this); }


int cmp_item_temporal::compare(cmp_item *ci)
{
  cmp_item_temporal *l_cmp= (cmp_item_temporal *)ci;
  return (value < l_cmp->value) ? -1 : ((value == l_cmp->value) ? 0 : 1);
}


bool Item_func_null_predicate::count_sargable_conds(void *arg)
{
  ((SELECT_LEX*) arg)->cond_count++;
  return 0;
}


  const Type_handler *type_handler() const
  { return &type_handler_bool; }


  bool pushable_cond_checker_for_derived(uchar *arg)
  {
    return excl_dep_on_table(*((table_map *)arg));
  }


bool Item_in_optimizer::fix_left(THD *thd)
{
  DBUG_ENTER("Item_in_optimizer::fix_left");
  /*
    Here we will store pointer on place of main storage of left expression.
    For usual IN (ALL/ANY) it is subquery left_expr.
    For other cases (MAX/MIN optimization, non-transformed EXISTS (10.0))
    it is args[0].
  */
  Item **ref0= args;
  if (!invisible_mode())
  {
    /*
       left_expr->fix_fields() may cause left_expr to be substituted for
       another item. (e.g. an Item_field may be changed into Item_ref). This
       transformation is undone at the end of statement execution (e.g. the
       Item_ref is deleted). However, Item_in_optimizer::args[0] may keep
       the pointer to the post-transformation item. Because of that, on the
       next execution we need to copy args[1]->left_expr again.
    */
    ref0= &(((Item_in_subselect *)args[1])->left_expr);
    args[0]= ((Item_in_subselect *)args[1])->left_expr;
  }
  if ((*ref0)->fix_fields_if_needed(thd, ref0) ||
      (!cache && !(cache= (*ref0)->get_cache(thd))))
    DBUG_RETURN(1);
  /*
    During fix_field() expression could be substituted.
    So we copy changes before use
  */
  if (args[0] != (*ref0))
    args[0]= (*ref0);
  DBUG_PRINT("info", ("actual fix fields"));

  cache->setup(thd, args[0]);
  if (cache->cols() == 1)
  {
    DBUG_ASSERT(args[0]->type() != ROW_ITEM);
    /*
      Note: there can be cases when used_tables()==0 && !const_item(). See
      Item_sum::update_used_tables for details.
    */
    if ((used_tables_cache= args[0]->used_tables()) || !args[0]->const_item())
      cache->set_used_tables(OUTER_REF_TABLE_BIT);
    else
      cache->set_used_tables(0);
  }
  else
  {
    uint n= cache->cols();
    for (uint i= 0; i < n; i++)
    {
      /* Check that the expression (part of row) do not contain a subquery */
      if (args[0]->element_index(i)->walk(&Item::is_subquery_processor, 0, 0))
      {
        my_error(ER_NOT_SUPPORTED_YET, MYF(0),
                 "SUBQUERY in ROW in left expression of IN/ALL/ANY");
        DBUG_RETURN(1);
      }
      Item *element=args[0]->element_index(i);
      if (element->used_tables() || !element->const_item())
      {
	((Item_cache *)cache->element_index(i))->
          set_used_tables(OUTER_REF_TABLE_BIT);
        cache->set_used_tables(OUTER_REF_TABLE_BIT);
      }
      else
	((Item_cache *)cache->element_index(i))->set_used_tables(0);
    }
    used_tables_cache= args[0]->used_tables();
  }
  eval_not_null_tables(NULL);
  copy_with_sum_func(args[0]);
  with_param= args[0]->with_param || args[1]->with_param;
  with_field= args[0]->with_field;
  if ((const_item_cache= args[0]->const_item()))
  {
    cache->store(args[0]);
    cache->cache_value();
  }
  if (args[1]->is_fixed())
  {
    /* to avoid overriding is called to update left expression */
    used_tables_and_const_cache_join(args[1]);
    join_with_sum_func(args[1]);
  }
  DBUG_RETURN(0);
}


  Item_type_holder(THD *thd,
                   Item *item,
                   const Type_handler *handler,
                   const Type_all_attributes *attr,
                   bool maybe_null_arg)
   :Item(thd),
    Type_handler_hybrid_field_type(handler),
    Type_geometry_attributes(handler, attr),
    enum_set_typelib(attr->get_typelib())
  {
    name= item->name;
    Type_std_attributes::set(*attr);
    maybe_null= maybe_null_arg;
  }


  Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
                             const Tmp_field_param *param)
  {

    /*
      create_tmp_field_ex() for this type of Items is called for:
      - CREATE TABLE ... SELECT
      - In ORDER BY: SELECT max(a) FROM t1 GROUP BY a ORDER BY 'const';
      - In CURSORS:
          DECLARE c CURSOR FOR SELECT 'test';
          OPEN c;
    */
    return tmp_table_field_from_field_type_maybe_null(table, src, param,
                                            type() == Item::NULL_ITEM);
  }


void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding)
{
  THD *thd= current_thd;

  Query_arena *arena, backup;
  arena= thd->activate_stmt_arena_if_needed(&backup);

  if (!transform_into_subq_checked)
  {
    if ((transform_into_subq= to_be_transformed_into_in_subq(thd)))
      thd->lex->current_select->in_funcs.push_back(this, thd->mem_root);
    transform_into_subq_checked= true;
  }

  if (arena)
    thd->restore_active_arena(arena, &backup);

  emb_on_expr_nest= embedding;
}


  virtual longlong val_int_unsigned_typecast()
  {
    return cast_to_int_type_handler()->Item_val_int_unsigned_typecast(this);
  }


  bool is_valid_limit_clause_variable_with_error() const
  {
    /*
      In case if the variable has an anchored data type, e.g.:
        DECLARE a TYPE OF t1.a;
      type_handler() is set to &type_handler_null and this
      function detects such variable as not valid in LIMIT.
    */
    if (type_handler()->is_limit_clause_valid_type())
      return true;
    my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
    return false;
  }


  Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg)
  { return convert_to_basic_const_item(thd); }


void Item_func_nullif::print(String *str, enum_query_type query_type)
{
  /*
    NULLIF(a,b) is implemented according to the SQL standard as a short for
    CASE WHEN a=b THEN NULL ELSE a END

    The constructor of Item_func_nullif sets args[0] and args[2] to the
    same item "a", and sets args[1] to "b".

    If "this" is a part of a WHERE or ON condition, then:
    - the left "a" is a subject to equal field propagation with ANY_SUBST.
    - the right "a" is a subject to equal field propagation with IDENTITY_SUBST.
    Therefore, after equal field propagation args[0] and args[2] can point
    to different items.
  */
  if ((query_type & QT_ITEM_ORIGINAL_FUNC_NULLIF) ||
      (arg_count == 2) ||
      (args[0] == args[2]))
  {
    /*
      If QT_ITEM_ORIGINAL_FUNC_NULLIF is requested,
      that means we want the original NULLIF() representation,
      e.g. when we are in:
        SHOW CREATE {VIEW|FUNCTION|PROCEDURE}

      The original representation is possible only if
      args[0] and args[2] still point to the same Item.

      The caller must never pass call print() with QT_ITEM_ORIGINAL_FUNC_NULLIF
      if an expression has undergone some optimization
      (e.g. equal field propagation done in optimize_cond()) already and
      NULLIF() potentially has two different representations of "a":
      - one "a" for comparison
      - another "a" for the returned value!
    */
    DBUG_ASSERT(arg_count == 2 ||
                args[0] == args[2] || current_thd->lex->context_analysis_only);
    str->append(func_name());
    str->append('(');
    if (arg_count == 2)
      args[0]->print(str, query_type);
    else
      args[2]->print(str, query_type);
    str->append(',');
    args[1]->print(str, query_type);
    str->append(')');
  }
  else
  {
    /*
      args[0] and args[2] are different items.
      This is possible after WHERE optimization (equal fields propagation etc),
      e.g. in EXPLAIN EXTENDED or EXPLAIN FORMAT=JSON.
      As it's not possible to print as a function with 2 arguments any more,
      do it in the CASE style.
    */
    str->append(STRING_WITH_LEN("(case when "));
    args[0]->print(str, query_type);
    str->append(STRING_WITH_LEN(" = "));
    args[1]->print(str, query_type);
    str->append(STRING_WITH_LEN(" then NULL else "));
    args[2]->print(str, query_type);
    str->append(STRING_WITH_LEN(" end)"));
  }
}


bool is_bool_literal() const { return true; }


bool Arg_comparator::set_cmp_func_native()
{
  THD *thd= current_thd;
  m_compare_collation= &my_charset_numeric;
  func= is_owner_equal_func() ? &Arg_comparator::compare_e_native :
                                &Arg_comparator::compare_native;
  a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
  b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
  return false;
}


  Item *get_copy(THD *thd)
  { return get_item_copy<Item_field_row>(thd, this); }


Sql_mode_dependency Item_func_like::value_depends_on_sql_mode() const
{
  if (!args[1]->value_depends_on_sql_mode_const_item())
    return Item_func::value_depends_on_sql_mode();
  StringBuffer<64> patternbuf;
  String *pattern= args[1]->val_str_ascii(&patternbuf);
  if (!pattern || !pattern->length())
    return Sql_mode_dependency();                  // Will return NULL or 0
  DBUG_ASSERT(pattern->charset()->mbminlen == 1);
  if (pattern->ptr()[pattern->length() - 1] != '%')
    return Item_func::value_depends_on_sql_mode();
  return ((args[0]->value_depends_on_sql_mode() |
           args[1]->value_depends_on_sql_mode()) &
          Sql_mode_dependency(~0, ~MODE_PAD_CHAR_TO_FULL_LENGTH)).
         soft_to_hard();
}


Item *Item_func_ge::negated_item(THD *thd) /* a >= b  ->  a < b */
{
  return new (thd->mem_root) Item_func_lt(thd, args[0], args[1]);
}


  const Type_handler *fixed_type_handler() const
  { return &type_handler_bool; }


  const Type_handler *type_handler() const
  {
    return Type_handler::get_handler_by_field_type(date_time_field_type);
  }


void Regexp_processor_pcre::set_recursion_limit(THD *thd)
{
  long stack_used;
  DBUG_ASSERT(thd == current_thd);
  stack_used= available_stack_size(thd->thread_stack, &stack_used);
  m_pcre_extra.match_limit_recursion=
    (ulong)((my_thread_stack_size - STACK_MIN_SIZE - stack_used)/my_pcre_frame_size);
}


  Field *create_table_field_from_handler(TABLE *table)
  {
    const Type_handler *h= type_handler();
    return h->make_and_init_table_field(&name, Record_addr(maybe_null),
                                        *this, table);
  }


bool Item_func_between::eval_not_null_tables(void *opt_arg)
{
  if (Item_func_opt_neg::eval_not_null_tables(NULL))
    return 1;

  /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */
  if (pred_level && !negated)
    return 0;

  /* not_null_tables_cache == union(T1(e), intersection(T1(e1),T1(e2))) */
  not_null_tables_cache= (args[0]->not_null_tables() |
                          (args[1]->not_null_tables() &
                           args[2]->not_null_tables()));
  return 0;
}


void Item_func_like::print(String *str, enum_query_type query_type)
{
  args[0]->print_parenthesised(str, query_type, precedence());
  str->append(' ');
  if (negated)
    str->append(STRING_WITH_LEN(" not "));
  str->append(func_name());
  str->append(' ');
  if (escape_used_in_parsing)
  {
    args[1]->print_parenthesised(str, query_type, precedence());
    str->append(STRING_WITH_LEN(" escape "));
    escape_item->print_parenthesised(str, query_type, higher_precedence());
  }
  else
    args[1]->print_parenthesised(str, query_type, higher_precedence());
}


  virtual bool val_native(THD *thd, Native *to)
  {
   /*
     The default implementation for the Items that do not need native format:
     - Item_basic_value (default implementation)
     - Item_copy
     - Item_exists_subselect
     - Item_sum_field
     - Item_sum_or_func (default implementation)
     - Item_proc
     - Item_type_holder (as val_xxx() are never called for it);

     These hybrid Item types override val_native():
     - Item_field
     - Item_param
     - Item_sp_variable
     - Item_ref
     - Item_cache_wrapper
     - Item_direct_ref
     - Item_direct_view_ref
     - Item_ref_null_helper
     - Item_name_const
     - Item_time_literal
     - Item_sum_or_func
         Note, these hybrid type Item_sum_or_func descendants
         override the default implementation:
         * Item_sum_hybrid
         * Item_func_hybrid_field_type
         * Item_func_min_max
         * Item_func_sp
         * Item_func_last_value
         * Item_func_rollup_const
   */
    DBUG_ASSERT(0);
    return null_value= true;
  }


longlong Item_cond_or::val_int()
{
  DBUG_ASSERT(fixed == 1);
  List_iterator_fast<Item> li(list);
  Item *item;
  null_value=0;
  while ((item=li++))
  {
    if (item->val_bool())
    {
      null_value=0;
      return 1;
    }
    if (item->null_value)
      null_value=1;
  }
  return 0;
}


static inline int cmp_longs (longlong a_val, longlong b_val)
{
  return a_val < b_val ? -1 : a_val == b_val ? 0 : 1;
}


  bool check_is_evaluable_expression_or_error()
  {
    if (is_evaluable_expression())
      return false; // Ok
    raise_error_not_evaluable();
    return true;    // Error
  }


static int cmp_row(void *cmp_arg, cmp_item_row *a, cmp_item_row *b)
{
  return a->compare(b);
}


Item *Item_func_decode_oracle::find_item()
{
  uint idx;
  if (!Predicant_to_list_comparator::cmp_nulls_equal(current_thd, this, &idx))
    return args[idx + when_count()];
  Item **pos= Item_func_decode_oracle::else_expr_addr();
  return pos ? pos[0] : 0;
}


  const double *const_ptr_double() const
  { return can_return_const_value(REAL_RESULT) ? &value.real : NULL; }


SEL_TREE *Item_func_like::get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
{
  MEM_ROOT *tmp_root= param->mem_root;
  param->thd->mem_root= param->old_root;
  bool sargable_pattern= with_sargable_pattern();
  param->thd->mem_root= tmp_root;
  return sargable_pattern ?
    Item_bool_func2::get_mm_tree(param, cond_ptr) :
    Item_func::get_mm_tree(param, cond_ptr);
}


virtual bool const_is_null() const { return false; }


  Field *create_tmp_field_ex_simple(TABLE *table,
                                    Tmp_field_src *src,
                                    const Tmp_field_param *param)
  {
    DBUG_ASSERT(!param->make_copy_field());
    DBUG_ASSERT(!is_result_field());
    DBUG_ASSERT(type() != NULL_ITEM);
    return tmp_table_field_from_field_type(table);
  }


  longlong val_time_packed(THD *thd)
  {
    DBUG_ASSERT(0);
    return 0;
  }


bool Item_in_optimizer::invisible_mode()
{
  /* MAX/MIN transformed or EXISTS->IN prepared => do nothing */
 return (args[1]->type() != Item::SUBSELECT_ITEM ||
         ((Item_subselect *)args[1])->substype() ==
         Item_subselect::EXISTS_SUBS);
}


Item_bool(THD *thd, bool i) :Item_int(thd, (longlong) i, 1) { }


  Item *get_copy(THD *thd)
  { return get_item_copy<Item_float>(thd, this); }


  Field *tmp_table_field_from_field_type(TABLE *table)
  {
    DBUG_ASSERT(is_fixed());
    const Type_handler *h= type_handler()->type_handler_for_tmp_table(this);
    return h->make_and_init_table_field(&name, Record_addr(maybe_null),
                                        *this, table);
  }


bool Item_func_between::fix_length_and_dec()
{
  max_length= 1;

  /*
    As some compare functions are generated after sql_yacc,
    we have to check for out of memory conditions here
  */
  if (!args[0] || !args[1] || !args[2])
    return TRUE;
  if (m_comparator.aggregate_for_comparison(Item_func_between::func_name(),
                                            args, 3, false))
  {
    DBUG_ASSERT(current_thd->is_error());
    return TRUE;
  }

  return m_comparator.type_handler()->
    Item_func_between_fix_length_and_dec(this);
}


int cmp_item_decimal::compare(cmp_item *arg)
{
  cmp_item_decimal *l_cmp= (cmp_item_decimal*) arg;
  return my_decimal_cmp(&value, &l_cmp->value);
}


  virtual Item *grouping_field_transformer_for_where(THD *thd, uchar *arg)
  { return this; }


  double val_real()
  {
    return to_datetime(current_thd).to_double();
  }


  Item_int(THD *thd, longlong i,size_t length= MY_INT64_NUM_DECIMAL_DIGITS):
    Item_num(thd), value(i)
    { max_length=(uint32)length; }


bool Item_func_not::fix_fields(THD *thd, Item **ref)
{
  args[0]->under_not(this);
  if (args[0]->type() == FIELD_ITEM)
  {
    /* replace  "NOT <field>" with "<field> == 0" */
    Query_arena backup, *arena;
    Item *new_item;
    bool rc= TRUE;
    arena= thd->activate_stmt_arena_if_needed(&backup);
    if ((new_item= new (thd->mem_root) Item_func_eq(thd, args[0], new (thd->mem_root) Item_int(thd, 0, 1))))
    {
      new_item->name= name;
      rc= (*ref= new_item)->fix_fields(thd, ref);
    }
    if (arena)
      thd->restore_active_arena(arena, &backup);
    return rc;
  }
  return Item_func::fix_fields(thd, ref);
}


  virtual bool val_native_result(THD *thd, Native *to)
  {
    return val_native(thd, to);
  }


  const Type_handler *type_handler_for_comparison() const
  {
    return type_handler()->type_handler_for_comparison();
  }


bool Arg_comparator::set_cmp_func_datetime()
{
  THD *thd= current_thd;
  m_compare_collation= &my_charset_numeric;
  func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime :
                                &Arg_comparator::compare_datetime;
  a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
  b= cache_converted_constant(thd, b, &b_cache, compare_type_handler());
  return false;
}


Field *default_field() const { return m_default_field; }


int cmp_item_decimal::cmp_not_null(const Value *val)
{
  DBUG_ASSERT(!val->is_null());
  DBUG_ASSERT(val->is_decimal());
  return my_decimal_cmp(&value, &val->m_decimal);
}


double Item_func_coalesce::real_op()
{
  DBUG_ASSERT(fixed == 1);
  null_value=0;
  for (uint i=0 ; i < arg_count ; i++)
  {
    double res= args[i]->val_real();
    if (!args[i]->null_value)
      return res;
  }
  null_value=1;
  return 0;
}


virtual bool is_bool_literal() const { return false; }


enum Type type() const { return CONTEXTUALLY_TYPED_VALUE_ITEM; }


  Item_splocal_with_delayed_data_type(THD *thd,
                                      const Sp_rcontext_handler *rh,
                                      const LEX_CSTRING *sp_var_name,
                                      uint sp_var_idx,
                                      uint pos_in_q, uint len_in_q)
   :Item_splocal(thd, rh, sp_var_name, sp_var_idx, &type_handler_null,
                 pos_in_q, len_in_q)
  { }


  const Type_handler *type_handler() const
  { return type_handler_long_or_longlong(); }


  bool pushable_equality_checker_for_derived(uchar *arg)
  {
    return (used_tables() == *((table_map *)arg));
  }


int cmp_item_row::compare(cmp_item *c)
{
  cmp_item_row *l_cmp= (cmp_item_row *) c;
  for (uint i=0; i < n; i++)
  {
    int res;
    if ((res= comparators[i]->compare(l_cmp->comparators[i])))
      return res;
  }
  return 0;
}


  double val_real()
  {
    return has_value() ? Time(this).to_double() : 0;
  }


bool Item_func_case_simple::prepare_predicant_and_values(THD *thd,
                                                         uint *found_types,
                                                         bool nulls_equal)
{
  bool have_null= false;
  uint type_cnt;
  Type_handler_hybrid_field_type tmp;
  uint ncases= when_count();
  add_predicant(this, 0);
  for (uint i= 0 ; i < ncases; i++)
  {
    if (nulls_equal ?
        add_value("case..when", this, i + 1) :
        add_value_skip_null("case..when", this, i + 1, &have_null))
      return true;
  }
  all_values_added(&tmp, &type_cnt, &m_found_types);
#ifndef DBUG_OFF
  Predicant_to_list_comparator::debug_print(thd);
#endif
  return false;
}


Item *Item_func_isnotnull::neg_transformer(THD *thd)
{
  Item *item= new (thd->mem_root) Item_func_isnull(thd, args[0]);
  return item;
}


bool Item_func_nullif::walk(Item_processor processor,
                            bool walk_subquery, void *arg)
{
  /*
    No needs to iterate through args[2] when it's just a copy of args[0].
    See MDEV-9712 Performance degradation of nested NULLIF
  */
  uint tmp_count= arg_count == 2 || args[0] == args[2] ? 2 : 3;
  for (uint i= 0; i < tmp_count; i++)
  {
    if (args[i]->walk(processor, walk_subquery, arg))
      return true;
  }
  return (this->*processor)(arg);
}


bool Item_func_in::count_sargable_conds(void *arg)
{
  ((SELECT_LEX*) arg)->cond_count++;
  return 0;
}


  int save_in_field(Field *field, bool no_conversions)
  {
    DBUG_ASSERT(sane());
    if (null_value)
      return set_field_to_null(field);
    Timestamp_or_zero_datetime_native native(m_value, decimals);
    return native.save_in_field(field, decimals);
  }


Item* Item_func_case_simple::propagate_equal_fields(THD *thd,
                                                    const Context &ctx,
                                                    COND_EQUAL *cond)
{
  const Type_handler *first_expr_cmp_handler;

  first_expr_cmp_handler= args[0]->type_handler_for_comparison();
  /*
    Cannot replace the CASE (the switch) argument if
    there are multiple comparison types were found, or found a single
    comparison type that is not equal to args[0]->cmp_type().

    - Example: multiple comparison types, can't propagate:
        WHERE CASE str_column
              WHEN 'string' THEN TRUE
              WHEN 1 THEN TRUE
              ELSE FALSE END;

    - Example: a single incompatible comparison type, can't propagate:
        WHERE CASE str_column
              WHEN DATE'2001-01-01' THEN TRUE
              ELSE FALSE END;

    - Example: a single incompatible comparison type, can't propagate:
        WHERE CASE str_column
              WHEN 1 THEN TRUE
              ELSE FALSE END;

    - Example: a single compatible comparison type, ok to propagate:
        WHERE CASE str_column
              WHEN 'str1' THEN TRUE
              WHEN 'str2' THEN TRUE
              ELSE FALSE END;
  */
  if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type()))
    propagate_and_change_item_tree(thd, &args[0], cond,
      Context(ANY_SUBST, first_expr_cmp_handler, cmp_collation.collation));

  /*
    These arguments are in comparison.
    Allow invariants of the same value during propagation.
    Note, as we pass ANY_SUBST, none of the WHEN arguments will be
    replaced to zero-filled constants (only IDENTITY_SUBST allows this).
    Such a change for WHEN arguments would require rebuilding cmp_items.
  */
  uint i, count= when_count();
  for (i= 1; i <= count; i++)
  {
    Type_handler_hybrid_field_type tmp(first_expr_cmp_handler);
    if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison()))
      propagate_and_change_item_tree(thd, &args[i], cond,
        Context(ANY_SUBST, tmp.type_handler(), cmp_collation.collation));
  }

  // THEN and ELSE arguments (they are not in comparison)
  for (; i < arg_count; i++)
    propagate_and_change_item_tree(thd, &args[i], cond, Context_identity());

  return this;
}


bool Item_func_interval::fix_length_and_dec()
{
  uint rows= row->cols();

  use_decimal_comparison= ((row->element_index(0)->result_type() ==
                            DECIMAL_RESULT) ||
                           (row->element_index(0)->result_type() ==
                            INT_RESULT));
  if (rows > 8)
  {
    bool not_null_consts= TRUE;

    for (uint i= 1; not_null_consts && i < rows; i++)
    {
      Item *el= row->element_index(i);
      not_null_consts&= el->const_item() && !el->is_null();
    }

    if (not_null_consts)
    {
      intervals= (interval_range*) current_thd->alloc(sizeof(interval_range) *
                                                         (rows - 1));
      if (!intervals)
        return TRUE;

      if (use_decimal_comparison)
      {
        for (uint i= 1; i < rows; i++)
        {
          Item *el= row->element_index(i);
          interval_range *range= intervals + (i-1);
          if ((el->result_type() == DECIMAL_RESULT) ||
              (el->result_type() == INT_RESULT))
          {
            range->type= DECIMAL_RESULT;
            range->dec.init();
            my_decimal *dec= el->val_decimal(&range->dec);
            if (dec != &range->dec)
            {
              range->dec= *dec;
            }
          }
          else
          {
            range->type= REAL_RESULT;
            range->dbl= el->val_real();
          }
        }
      }
      else
      {
        for (uint i= 1; i < rows; i++)
        {
          intervals[i-1].dbl= row->element_index(i)->val_real();
        }
      }
    }
  }
  maybe_null= 0;
  max_length= 2;
  used_tables_and_const_cache_join(row);
  not_null_tables_cache= row->not_null_tables();
  join_with_sum_func(row);
  with_param= with_param || row->with_param;
  with_field= with_field || row->with_field;
  return FALSE;
}


bool Item_func_decode_oracle::fix_length_and_dec()
{
  THD *thd= current_thd;
  return (aggregate_then_and_else_arguments(thd, when_count() + 1) ||
          aggregate_switch_and_when_arguments(thd, true));
}


void set_modify_item(bool to) { m_modify_item= to; }