int Arg_comparator::compare_int_signed_unsigned()
{
  longlong sval1= (*a)->val_int();
  if (!(*a)->null_value)
  {
    ulonglong uval2= (ulonglong)(*b)->val_int();
    if (!(*b)->null_value)
    {
      if (set_null)
        owner->null_value= 0;
      if (sval1 < 0 || (ulonglong)sval1 < uval2)
        return -1;
      if ((ulonglong)sval1 == uval2)
        return 0;
      return 1;
    }
  }
  if (set_null)
    owner->null_value= 1;
  return -1;
}


virtual Item* build_clone(THD *thd) { return get_copy(thd); }

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.


static int check_func_long(THD *thd, struct st_mysql_sys_var *var,
                          void *save, st_mysql_value *value)
{
  my_bool fixed1, fixed2;
  long long orig, val;
  struct my_option options;
  value->val_int(value, &orig);
  val= orig;
  plugin_opt_set_limits(&options, var);

  if (var->flags & PLUGIN_VAR_UNSIGNED)
  {
    if ((fixed1= (!value->is_unsigned(value) && val < 0)))
      val=0;
    *(ulong *)save= (ulong) getopt_ull_limit_value((ulonglong) val, &options,
                                                   &fixed2);
  }
  else
  {
    if ((fixed1= (value->is_unsigned(value) && val < 0)))
      val=LONGLONG_MAX;
    *(long *)save= (long) getopt_ll_limit_value(val, &options, &fixed2);
  }

  return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
                              value->is_unsigned(value), (longlong) orig);
}

MDEV-26323 use-after-poison issue of MariaDB server


static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
                               int *argc, char **argv)
{
  struct sys_var_chain chain= { NULL, NULL };
  bool disable_plugin;
  enum_plugin_load_option plugin_load_option= tmp->load_option;

  MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ?
                      &tmp->mem_root : &plugin_vars_mem_root;
  st_mysql_sys_var **opt;
  my_option *opts= NULL;
  int error= 1;
  struct st_bookmark *var;
  uint len=0, count= EXTRA_OPTIONS;
  st_ptr_backup *tmp_backup= 0;
  DBUG_ENTER("test_plugin_options");
  DBUG_ASSERT(tmp->plugin && tmp->name.str);

  if (tmp->plugin->system_vars || (*argc > 1))
  {
    for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
    {
      len++;
      if (!((*opt)->flags & PLUGIN_VAR_NOCMDOPT))
        count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */
    }

    if (!(opts= (my_option*) alloc_root(tmp_root, sizeof(my_option) * count)))
    {
      sql_print_error("Out of memory for plugin '%s'.", tmp->name.str);
      DBUG_RETURN(-1);
    }
    bzero(opts, sizeof(my_option) * count);

    if (construct_options(tmp_root, tmp, opts))
    {
      sql_print_error("Bad options for plugin '%s'.", tmp->name.str);
      DBUG_RETURN(-1);
    }

    if (tmp->plugin->system_vars)
    {
      tmp_backup= (st_ptr_backup *)my_alloca(len * sizeof(tmp_backup[0]));
      DBUG_ASSERT(tmp->nbackups == 0);
      DBUG_ASSERT(tmp->ptr_backup == 0);

      for (opt= tmp->plugin->system_vars; *opt; opt++)
      {
        st_mysql_sys_var *o= *opt;
        char *varname;
        sys_var *v;

        if (o->flags & PLUGIN_VAR_NOSYSVAR)
          continue;

        tmp_backup[tmp->nbackups++].save(&o->name);
        if ((var= find_bookmark(tmp->name.str, o->name, o->flags)))
        {
          varname= var->key + 1;
          var->loaded= TRUE;
        }
        else
        {
          var= NULL;
          len= tmp->name.length + strlen(o->name) + 2;
          varname= (char*) alloc_root(mem_root, len);
          strxmov(varname, tmp->name.str, "-", o->name, NullS);
          my_casedn_str(&my_charset_latin1, varname);
          convert_dash_to_underscore(varname, len-1);
        }
        v= new (mem_root) sys_var_pluginvar(&chain, varname, tmp, o);
        v->test_load= (var ? &var->loaded : &static_unload);
        DBUG_ASSERT(static_unload == FALSE);

        if (!(o->flags & PLUGIN_VAR_NOCMDOPT))
        {
          // update app_type, used for I_S.SYSTEM_VARIABLES
          for (my_option *mo=opts; mo->name; mo++)
            if (mo->app_type == o)
              mo->app_type= v;
        }
      }

      if (tmp->nbackups)
      {
        size_t bytes= tmp->nbackups * sizeof(tmp->ptr_backup[0]);
        tmp->ptr_backup= (st_ptr_backup *)alloc_root(mem_root, bytes);
        if (!tmp->ptr_backup)
        {
          restore_ptr_backup(tmp->nbackups, tmp_backup);
          my_afree(tmp_backup);
          goto err;
        }
        memcpy(tmp->ptr_backup, tmp_backup, bytes);
      }
      my_afree(tmp_backup);
    }

    /*
      We adjust the default value to account for the hardcoded exceptions
      we have set for the federated and ndbcluster storage engines.
    */
    if (!plugin_is_forced(tmp))
      opts[0].def_value= opts[1].def_value= plugin_load_option;

    error= handle_options(argc, &argv, opts, mark_changed);
    (*argc)++; /* add back one for the program name */

    if (error)
    {
       sql_print_error("Parsing options for plugin '%s' failed.",
                       tmp->name.str);
       goto err;
    }
    /*
     Set plugin loading policy from option value. First element in the option
     list is always the <plugin name> option value.
    */
    if (!plugin_is_forced(tmp))
      plugin_load_option= (enum_plugin_load_option) *(ulong*) opts[0].value;
  }

  disable_plugin= (plugin_load_option == PLUGIN_OFF);
  tmp->load_option= plugin_load_option;

  error= 1;

  /*
    If the plugin is disabled it should not be initialized.
  */
  if (disable_plugin)
  {
    if (global_system_variables.log_warnings)
      sql_print_information("Plugin '%s' is disabled.",
                            tmp->name.str);
    goto err;
  }

  if (tmp->plugin->system_vars)
  {
    for (opt= tmp->plugin->system_vars; *opt; opt++)
    {
      /*
        PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
        directly to values in the argv[] array. For plugins started at the
        server startup, argv[] array is allocated with load_defaults(), and
        freed when the server is shut down.  But for plugins loaded with
        INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
        free() at the end of mysql_install_plugin(). Which means we cannot
        allow any pointers into that area.
        Thus, for all plugins loaded after the server was started,
        we copy string values to a plugin's memroot.
      */
      if (mysqld_server_started &&
          (((*opt)->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_NOCMDOPT |
                             PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
      {
        sysvar_str_t* str= (sysvar_str_t *)*opt;
        if (*str->value)
          *str->value= strdup_root(mem_root, *str->value);
      }
    }

    if (chain.first)
    {
      chain.last->next = NULL;
      if (mysql_add_sys_var_chain(chain.first))
      {
        sql_print_error("Plugin '%s' has conflicting system variables",
                        tmp->name.str);
        goto err;
      }
      tmp->system_vars= chain.first;
    }
  }

  DBUG_RETURN(0);

err:
  if (opts)
    my_cleanup_options(opts);
  DBUG_RETURN(error);
}


void plugin_unlock(THD *thd, plugin_ref plugin)
{
  LEX *lex= thd ? thd->lex : 0;
  DBUG_ENTER("plugin_unlock");
  if (!plugin)
    DBUG_VOID_RETURN;
#ifdef DBUG_OFF
  /* built-in plugins don't need ref counting */
  if (!plugin_dlib(plugin))
    DBUG_VOID_RETURN;
#endif
  mysql_mutex_lock(&LOCK_plugin);
  intern_plugin_unlock(lex, plugin);
  reap_plugins();
  mysql_mutex_unlock(&LOCK_plugin);
  DBUG_VOID_RETURN;
}


uchar* sys_var_pluginvar::real_value_ptr(THD *thd, enum_var_type type)
{
  if (type == OPT_DEFAULT)
  {
    switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
    case PLUGIN_VAR_BOOL:
      thd->sys_var_tmp.my_bool_value= (my_bool)option.def_value;
      return (uchar*) &thd->sys_var_tmp.my_bool_value;
    case PLUGIN_VAR_INT:
      thd->sys_var_tmp.int_value= (int)option.def_value;
      return (uchar*) &thd->sys_var_tmp.int_value;
    case PLUGIN_VAR_LONG:
    case PLUGIN_VAR_ENUM:
      thd->sys_var_tmp.long_value= (long)option.def_value;
      return (uchar*) &thd->sys_var_tmp.long_value;
    case PLUGIN_VAR_LONGLONG:
    case PLUGIN_VAR_SET:
      return (uchar*) &option.def_value;
    case PLUGIN_VAR_STR:
      thd->sys_var_tmp.ptr_value= (void*) option.def_value;
      return (uchar*) &thd->sys_var_tmp.ptr_value;
    case PLUGIN_VAR_DOUBLE:
      thd->sys_var_tmp.double_value= getopt_ulonglong2double(option.def_value);
      return (uchar*) &thd->sys_var_tmp.double_value;
    default:
      DBUG_ASSERT(0);
    }
  }

  DBUG_ASSERT(thd || (type == OPT_GLOBAL));
  if (plugin_var->flags & PLUGIN_VAR_THDLOCAL)
  {
    if (type == OPT_GLOBAL)
      thd= NULL;

    return intern_sys_var_ptr(thd, *(int*) (plugin_var+1), false);
  }
  return *(uchar**) (plugin_var+1);
}


static void plugin_dl_del(struct st_plugin_dl *plugin_dl)
{
  DBUG_ENTER("plugin_dl_del");

  if (!plugin_dl)
    DBUG_VOID_RETURN;

  mysql_mutex_assert_owner(&LOCK_plugin);

  /* Do not remove this element, unless no other plugin uses this dll. */
  if (! --plugin_dl->ref_count)
  {
    free_plugin_mem(plugin_dl);
    bzero(plugin_dl, sizeof(struct st_plugin_dl));
  }

  DBUG_VOID_RETURN;
}


bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
                          const LEX_STRING *dl_arg)
{
  TABLE_LIST tables;
  TABLE *table;
  LEX_STRING dl= *dl_arg;
  bool error;
  int argc=orig_argc;
  char **argv=orig_argv;
  unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
  { MYSQL_AUDIT_GENERAL_CLASSMASK };
  DBUG_ENTER("mysql_install_plugin");

  tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
  if (!opt_noacl && check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
    DBUG_RETURN(TRUE);
  WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);

  /* need to open before acquiring LOCK_plugin or it will deadlock */
  if (! (table = open_ltable(thd, &tables, TL_WRITE,
                             MYSQL_LOCK_IGNORE_TIMEOUT)))
    DBUG_RETURN(TRUE);

  if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL))
  {
    report_error(REPORT_TO_USER, ER_PLUGIN_IS_NOT_LOADED, name->str);
    DBUG_RETURN(TRUE);
  }

  /*
    Pre-acquire audit plugins for events that may potentially occur
    during [UN]INSTALL PLUGIN.

    When audit event is triggered, audit subsystem acquires interested
    plugins by walking through plugin list. Evidently plugin list
    iterator protects plugin list by acquiring LOCK_plugin, see
    plugin_foreach_with_mask().

    On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
    rather for a long time.

    When audit event is triggered during [UN]INSTALL PLUGIN, plugin
    list iterator acquires the same lock (within the same thread)
    second time.

    This hack should be removed when LOCK_plugin is fixed so it
    protects only what it supposed to protect.

    See also mysql_uninstall_plugin() and initialize_audit_plugin()
  */
  if (mysql_audit_general_enabled())
    mysql_audit_acquire_plugins(thd, event_class_mask);

  mysql_mutex_lock(&LOCK_plugin);
  error= plugin_add(thd->mem_root, name, &dl, REPORT_TO_USER);
  if (error)
    goto err;

  if (name->str)
    error= finalize_install(thd, table, name, &argc, argv);
  else
  {
    st_plugin_dl *plugin_dl= plugin_dl_find(&dl);
    struct st_maria_plugin *plugin;
    for (plugin= plugin_dl->plugins; plugin->info; plugin++)
    {
      LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
      error|= finalize_install(thd, table, &str, &argc, argv);
    }
  }

  if (error)
  {
    reap_needed= true;
    reap_plugins();
  }
err:
  global_plugin_version++;
  mysql_mutex_unlock(&LOCK_plugin);
  if (argv)
    free_defaults(argv);
  DBUG_RETURN(error);

WSREP_ERROR_LABEL:
  DBUG_RETURN(TRUE);
}


static inline void convert_underscore_to_dash(char *str, int len)
{
  for (char *p= str; p <= str+len; p++)
    if (*p == '_')
      *p= '-';
}


static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var,
                             void *tgt, const void *save)
{
  *(longlong *)tgt= *(ulonglong *) save;
}


static int *mysql_sys_var_int(THD* thd, int offset)
{
  return (int *) intern_sys_var_ptr(thd, offset, true);
}


static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin)
{
  uint i;
  struct st_plugin_int *tmp;
  DBUG_ENTER("plugin_insert_or_reuse");
  for (i= 0; i < plugin_array.elements; i++)
  {
    tmp= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
    if (tmp->state == PLUGIN_IS_FREED)
    {
      memcpy(tmp, plugin, sizeof(struct st_plugin_int));
      DBUG_RETURN(tmp);
    }
  }
  if (insert_dynamic(&plugin_array, (uchar*)&plugin))
    DBUG_RETURN(0);
  tmp= *dynamic_element(&plugin_array, plugin_array.elements - 1,
                        struct st_plugin_int **)=
       (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)plugin,
                                            sizeof(struct st_plugin_int));
  DBUG_RETURN(tmp);
}


void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root)
{
  struct st_plugin_int *p;
  my_option *opt;

  if (!initialized)
    return;

  for (uint idx= 0; idx < plugin_array.elements; idx++)
  {
    p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);

    if (!(opt= construct_help_options(mem_root, p)))
      continue;

    /* Only options with a non-NULL comment are displayed in help text */
    for (;opt->name; opt++)
      if (opt->comment)
        insert_dynamic(options, (uchar*) opt);
  }
}


static my_option *construct_help_options(MEM_ROOT *mem_root,
                                         struct st_plugin_int *p)
{
  st_mysql_sys_var **opt;
  my_option *opts;
  uint count= EXTRA_OPTIONS;
  DBUG_ENTER("construct_help_options");

  for (opt= p->plugin->system_vars; opt && *opt; opt++, count+= 2)
    ;

  if (!(opts= (my_option*) alloc_root(mem_root, sizeof(my_option) * count)))
    DBUG_RETURN(NULL);

  bzero(opts, sizeof(my_option) * count);

  /**
    some plugin variables (those that don't have PLUGIN_VAR_NOSYSVAR flag)
    have their names prefixed with the plugin name. Restore the names here
    to get the correct (not double-prefixed) help text.
    We won't need @@sysvars anymore and don't care about their proper names.
  */
  restore_ptr_backup(p->nbackups, p->ptr_backup);

  if (construct_options(mem_root, p, opts))
    DBUG_RETURN(NULL);

  DBUG_RETURN(opts);
}


static void plugin_del(struct st_plugin_int *plugin)
{
  DBUG_ENTER("plugin_del");
  mysql_mutex_assert_owner(&LOCK_plugin);
  /* Free allocated strings before deleting the plugin. */
  plugin_vars_free_values(plugin->system_vars);
  restore_ptr_backup(plugin->nbackups, plugin->ptr_backup);
  if (plugin->plugin_dl)
  {
    my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
    plugin_dl_del(plugin->plugin_dl);
    plugin->state= PLUGIN_IS_FREED;
    free_root(&plugin->mem_root, MYF(0));
  }
  else
    plugin->state= PLUGIN_IS_UNINITIALIZED;
  DBUG_VOID_RETURN;
}


static long long *mysql_sys_var_longlong(THD* thd, int offset)
{
  return (long long *) intern_sys_var_ptr(thd, offset, true);
}


bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
                       plugin_foreach_func *func, void *arg)
{
  bool err= 0;

  if (dl)
  {
    mysql_mutex_lock(&LOCK_plugin);
    st_plugin_dl *plugin_dl= plugin_dl_add(dl, REPORT_TO_USER);
    mysql_mutex_unlock(&LOCK_plugin);

    if (!plugin_dl)
      return 1;

    err= plugin_dl_foreach_internal(thd, plugin_dl, plugin_dl->plugins,
                                    func, arg);

    mysql_mutex_lock(&LOCK_plugin);
    plugin_dl_del(plugin_dl);
    mysql_mutex_unlock(&LOCK_plugin);
  }
  else
  {
    struct st_maria_plugin **builtins;
    for (builtins= mysql_mandatory_plugins; !err && *builtins; builtins++)
      err= plugin_dl_foreach_internal(thd, 0, *builtins, func, arg);
    for (builtins= mysql_optional_plugins; !err && *builtins; builtins++)
      err= plugin_dl_foreach_internal(thd, 0, *builtins, func, arg);
  }
  return err;
}


void wsrep_plugins_pre_init()
{
  wsrep_dummy_plugin_ptr= &wsrep_dummy_plugin;
  wsrep_dummy_plugin.state= PLUGIN_IS_DISABLED;
  global_system_variables.table_plugin=
    plugin_int_to_ref(wsrep_dummy_plugin_ptr);
}


static int check_func_str(THD *thd, struct st_mysql_sys_var *var,
                          void *save, st_mysql_value *value)
{
  char buff[STRING_BUFFER_USUAL_SIZE];
  const char *str;
  int length;

  length= sizeof(buff);
  if ((str= value->val_str(value, buff, &length)))
    str= thd->strmake(str, length);
  *(const char**)save= str;
  return 0;
}


static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *)
{
  return 0;
}


bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
                       int type, uint state_mask, void *arg)
{
  uint idx, total= 0;
  struct st_plugin_int *plugin;
  plugin_ref *plugins;
  my_bool res= FALSE;
  DBUG_ENTER("plugin_foreach_with_mask");

  if (!initialized)
    DBUG_RETURN(FALSE);

  mysql_mutex_lock(&LOCK_plugin);
  /*
    Do the alloca out here in case we do have a working alloca:
    leaving the nested stack frame invalidates alloca allocation.
  */
  if (type == MYSQL_ANY_PLUGIN)
  {
    plugins= (plugin_ref*) my_alloca(plugin_array.elements * sizeof(plugin_ref));
    for (idx= 0; idx < plugin_array.elements; idx++)
    {
      plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
      if ((plugins[total]= intern_plugin_lock(0, plugin_int_to_ref(plugin),
                                              state_mask)))
        total++;
    }
  }
  else
  {
    HASH *hash= plugin_hash + type;
    plugins= (plugin_ref*) my_alloca(hash->records * sizeof(plugin_ref));
    for (idx= 0; idx < hash->records; idx++)
    {
      plugin= (struct st_plugin_int *) my_hash_element(hash, idx);
      if ((plugins[total]= intern_plugin_lock(0, plugin_int_to_ref(plugin),
                                              state_mask)))
        total++;
    }
  }
  mysql_mutex_unlock(&LOCK_plugin);

  for (idx= 0; idx < total; idx++)
  {
    /* It will stop iterating on first engine error when "func" returns TRUE */
    if ((res= func(thd, plugins[idx], arg)))
        break;
  }

  plugin_unlock_list(0, plugins, total);
  my_afree(plugins);
  DBUG_RETURN(res);
}


static bool register_builtin(struct st_maria_plugin *plugin,
                             struct st_plugin_int *tmp,
                             struct st_plugin_int **ptr)
{
  DBUG_ENTER("register_builtin");
  tmp->ref_count= 0;
  tmp->plugin_dl= 0;

  if (insert_dynamic(&plugin_array, (uchar*)&tmp))
    DBUG_RETURN(1);

  *ptr= *dynamic_element(&plugin_array, plugin_array.elements - 1,
                         struct st_plugin_int **)=
        (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)tmp,
                                             sizeof(struct st_plugin_int));

  if (my_hash_insert(&plugin_hash[plugin->type],(uchar*) *ptr))
    DBUG_RETURN(1);

  DBUG_RETURN(0);
}


static int check_func_set(THD *thd, struct st_mysql_sys_var *var,
                          void *save, st_mysql_value *value)
{
  char buff[STRING_BUFFER_USUAL_SIZE], *error= 0;
  const char *str;
  TYPELIB *typelib;
  ulonglong result;
  uint error_len= 0;                            // init as only set on error
  bool not_used;
  int length;

  if (var->flags & PLUGIN_VAR_THDLOCAL)
    typelib= ((thdvar_set_t*) var)->typelib;
  else
    typelib= ((sysvar_set_t*)var)->typelib;

  if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
  {
    length= sizeof(buff);
    if (!(str= value->val_str(value, buff, &length)))
      goto err;
    result= find_set(typelib, str, length, NULL,
                     &error, &error_len, &not_used);
    if (error_len)
      goto err;
  }
  else
  {
    if (value->val_int(value, (long long *)&result))
      goto err;
    if (unlikely((result >= (1ULL << typelib->count)) &&
                 (typelib->count < sizeof(long)*8)))
      goto err;
  }
  *(ulonglong*)save= result;
  return 0;
err:
  return 1;
}


static void update_func_int(THD *thd, struct st_mysql_sys_var *var,
                             void *tgt, const void *save)
{
  *(int *)tgt= *(int *) save;
}


static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
                               void *tgt, const void *save)
{
  *(double *) tgt= *(double *) save;
}


static void report_error(int where_to, uint error, ...)
{
  va_list args;
  DBUG_ASSERT(where_to & (REPORT_TO_USER | REPORT_TO_LOG));
  if (where_to & REPORT_TO_USER)
  {
    va_start(args, error);
    my_printv_error(error, ER(error), MYF(0), args);
    va_end(args);
  }
  if (where_to & REPORT_TO_LOG)
  {
    va_start(args, error);
    error_log_print(ERROR_LEVEL, ER_DEFAULT(error), args);
    va_end(args);
  }
}


static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
{
  /*
    we don't want to hold the LOCK_plugin mutex as it may cause
    deinitialization to deadlock if plugins have worker threads
    with plugin locks
  */
  mysql_mutex_assert_not_owner(&LOCK_plugin);

  if (plugin->plugin->status_vars)
  {
    /*
      historical ndb behavior caused MySQL plugins to specify
      status var names in full, with the plugin name prefix.
      this was never fixed in MySQL.
      MariaDB fixes that but supports MySQL style too.
    */
    SHOW_VAR *show_vars= plugin->plugin->status_vars;
    SHOW_VAR tmp_array[2]= {
      {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
      {0, 0, SHOW_UNDEF}
    };
    if (strncasecmp(show_vars->name, plugin->name.str, plugin->name.length))
      show_vars= tmp_array;

    remove_status_vars(show_vars);
  }

  if (plugin_type_deinitialize[plugin->plugin->type])
  {
    if ((*plugin_type_deinitialize[plugin->plugin->type])(plugin))
    {
      sql_print_error("Plugin '%s' of type %s failed deinitialization",
                      plugin->name.str, plugin_type_names[plugin->plugin->type].str);
    }
  }
  else if (plugin->plugin->deinit)
  {
    DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str));
    if (plugin->plugin->deinit(plugin))
    {
      DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
                             plugin->name.str));
    }
  }
  plugin->state= PLUGIN_IS_UNINITIALIZED;

  if (ref_check && plugin->ref_count)
    sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
                    plugin->name.str, plugin->ref_count);
  plugin_variables_deinit(plugin);
}


static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
{
  char buffer[FN_REFLEN];
  LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name;
  char *p= buffer;
  DBUG_ENTER("plugin_load_list");
  while (list)
  {
    if (p == buffer + sizeof(buffer) - 1)
    {
      sql_print_error("plugin-load parameter too long");
      DBUG_RETURN(TRUE);
    }

    switch ((*(p++)= *(list++))) {
    case '\0':
      list= NULL; /* terminate the loop */
      /* fall through */
    case ';':
#ifndef __WIN__
    case ':':     /* can't use this as delimiter as it may be drive letter */
#endif
      str->str[str->length]= '\0';
      if (str == &name)  // load all plugins in named module
      {
        if (!name.length)
        {
          p--;    /* reset pointer */
          continue;
        }

        dl= name;
        mysql_mutex_lock(&LOCK_plugin);
        free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
        name.str= 0; // load everything
        if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG))
          goto error;
      }
      else
      {
        free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
        mysql_mutex_lock(&LOCK_plugin);
        if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG))
          goto error;
      }
      mysql_mutex_unlock(&LOCK_plugin);
      name.length= dl.length= 0;
      dl.str= NULL; name.str= p= buffer;
      str= &name;
      continue;
    case '=':
    case '#':
      if (str == &name)
      {
        name.str[name.length]= '\0';
        str= &dl;
        str->str= p;
        continue;
      }
      /* fall through */
    default:
      str->length++;
      continue;
    }
  }
  DBUG_RETURN(FALSE);
error:
  mysql_mutex_unlock(&LOCK_plugin);
  if (name.str)
    sql_print_error("Couldn't load plugin '%s' from '%s'.",
                    name.str, dl.str);
  else
    sql_print_error("Couldn't load plugins from '%s'.", dl.str);
  DBUG_RETURN(TRUE);
}


static void plugin_load(MEM_ROOT *tmp_root)
{
  TABLE_LIST tables;
  TABLE *table;
  READ_RECORD read_record_info;
  int error;
  THD *new_thd= new THD(0);
  bool result;
  DBUG_ENTER("plugin_load");

  if (global_system_variables.log_warnings >= 9)
    sql_print_information("Initializing installed plugins");

  new_thd->thread_stack= (char*) &tables;
  new_thd->store_globals();
  new_thd->db= my_strdup("mysql", MYF(0));
  new_thd->db_length= 5;
  bzero((char*) &new_thd->net, sizeof(new_thd->net));
  tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("plugin"),
                        "plugin", TL_READ);
  tables.open_strategy= TABLE_LIST::OPEN_NORMAL;

  result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT);

  table= tables.table;
  if (result)
  {
    DBUG_PRINT("error",("Can't open plugin table"));
    if (!opt_help)
      sql_print_error("Could not open mysql.plugin table. "
                      "Some plugins may be not loaded");
    else
      sql_print_warning("Could not open mysql.plugin table. "
                        "Some options may be missing from the help text");
    goto end;
  }

  if (init_read_record(&read_record_info, new_thd, table, NULL, NULL, 1, 0,
                       FALSE))
  {
    sql_print_error("Could not initialize init_read_record; Plugins not "
                    "loaded");
    goto end;
  }
  table->use_all_columns();
  while (!(error= read_record_info.read_record(&read_record_info)))
  {
    DBUG_PRINT("info", ("init plugin record"));
    String str_name, str_dl;
    get_field(tmp_root, table->field[0], &str_name);
    get_field(tmp_root, table->field[1], &str_dl);

    LEX_STRING name= {(char *)str_name.ptr(), str_name.length()};
    LEX_STRING dl= {(char *)str_dl.ptr(), str_dl.length()};

    if (!name.length || !dl.length)
      continue;

    /*
      there're no other threads running yet, so we don't need a mutex.
      but plugin_add() before is designed to work in multi-threaded
      environment, and it uses mysql_mutex_assert_owner(), so we lock
      the mutex here to satisfy the assert
    */
    mysql_mutex_lock(&LOCK_plugin);
    plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG);
    free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
    mysql_mutex_unlock(&LOCK_plugin);
  }
  if (error > 0)
    sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno,
                           table->file->table_type());
  end_read_record(&read_record_info);
  table->mark_table_for_reopen();
  close_mysql_tables(new_thd);
end:
  delete new_thd;
  DBUG_VOID_RETURN;
}


static void restore_ptr_backup(uint n, st_ptr_backup *backup)
{
  while (n--)
    (backup++)->restore();
}


void thd_key_delete(MYSQL_THD_KEY_T *key)
{
  *key= INVALID_THD_KEY;
}


  uchar* session_value_ptr(THD *thd, const LEX_STRING *base)
  { return do_value_ptr(thd, OPT_SESSION, base); }


static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name)
{
  struct st_plugin_int *plugin;
  mysql_mutex_assert_owner(&LOCK_plugin);

  if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) ||
      plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING))
  {
    // maybe plugin is in mysql.plugin present so postpond the error
    plugin= NULL;
  }

  if (plugin)
  {
    if (!plugin->plugin_dl)
    {
      my_error(ER_PLUGIN_DELETE_BUILTIN, MYF(0));
      return 1;
    }
    if (plugin->load_option == PLUGIN_FORCE_PLUS_PERMANENT)
    {
      my_error(ER_PLUGIN_IS_PERMANENT, MYF(0), name->str);
      return 1;
    }

    plugin->state= PLUGIN_IS_DELETED;
    if (plugin->ref_count)
      push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
          WARN_PLUGIN_BUSY, ER_THD(thd, WARN_PLUGIN_BUSY));
    else
      reap_needed= true;
  }

  uchar user_key[MAX_KEY_LENGTH];
  table->use_all_columns();
  table->field[0]->store(name->str, name->length, system_charset_info);
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
  if (! table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
                                           HA_WHOLE_KEY, HA_READ_KEY_EXACT))
  {
    int error;
    /*
      We do not replicate the UNINSTALL PLUGIN statement. Disable binlogging
      of the delete from the plugin table, so that it is not replicated in
      row based mode.
    */
    tmp_disable_binlog(thd);
    error= table->file->ha_delete_row(table->record[0]);
    reenable_binlog(thd);
    if (error)
    {
      table->file->print_error(error, MYF(0));
      return 1;
    }
  }
  else if (!plugin)
  {
    my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
    return 1;
  }
  return 0;
}


static void plugin_vars_free_values(sys_var *vars)
{
  DBUG_ENTER("plugin_vars_free_values");

  for (sys_var *var= vars; var; var= var->next)
  {
    sys_var_pluginvar *piv= var->cast_pluginvar();
    if (piv &&
        ((piv->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
        (piv->plugin_var->flags & PLUGIN_VAR_MEMALLOC))
    {
      /* Free the string from global_system_variables. */
      char **valptr= (char**) piv->real_value_ptr(NULL, OPT_GLOBAL);
      DBUG_PRINT("plugin", ("freeing value for: '%s'  addr: %p",
                            var->name.str, valptr));
      my_free(*valptr);
      *valptr= NULL;
    }
  }
  DBUG_VOID_RETURN;
}


static void unlock_variables(THD *thd, struct system_variables *vars)
{
  intern_plugin_unlock(NULL, vars->table_plugin);
  intern_plugin_unlock(NULL, vars->tmp_table_plugin);
  intern_plugin_unlock(NULL, vars->enforced_table_plugin);
  vars->table_plugin= vars->tmp_table_plugin= vars->enforced_table_plugin= NULL;
}


static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc,
                                     uint state_mask= PLUGIN_IS_READY |
                                                      PLUGIN_IS_UNINITIALIZED |
                                                      PLUGIN_IS_DELETED)
{
  st_plugin_int *pi= plugin_ref_to_int(rc);
  DBUG_ENTER("intern_plugin_lock");

  mysql_mutex_assert_owner(&LOCK_plugin);

  if (pi->state & state_mask)
  {
    plugin_ref plugin;
#ifdef DBUG_OFF
    /*
      In optimized builds we don't do reference counting for built-in
      (plugin->plugin_dl == 0) plugins.
    */
    if (!pi->plugin_dl)
      DBUG_RETURN(pi);

    plugin= pi;
#else
    /*
      For debugging, we do an additional malloc which allows the
      memory manager and/or valgrind to track locked references and
      double unlocks to aid resolving reference counting problems.
    */
    if (!(plugin= (plugin_ref) my_malloc(sizeof(pi), MYF(MY_WME))))
      DBUG_RETURN(NULL);

    *plugin= pi;
#endif
    pi->ref_count++;
    DBUG_PRINT("lock",("thd: %p  plugin: \"%s\" LOCK ref_count: %d",
                       current_thd, pi->name.str, pi->ref_count));

    if (lex)
      insert_dynamic(&lex->plugins, (uchar*)&plugin);
    DBUG_RETURN(plugin);
  }
  DBUG_RETURN(NULL);
}


void plugin_mutex_init()
{
#ifdef HAVE_PSI_INTERFACE
  init_plugin_psi_keys();
#endif
  mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST);
}


static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
                             int *argc, char **argv, bool options_only)
{
  int ret= 1;
  DBUG_ENTER("plugin_initialize");

  mysql_mutex_assert_owner(&LOCK_plugin);
  uint state= plugin->state;
  DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED);

  mysql_mutex_unlock(&LOCK_plugin);

  mysql_prlock_wrlock(&LOCK_system_variables_hash);
  if (test_plugin_options(tmp_root, plugin, argc, argv))
    state= PLUGIN_IS_DISABLED;
  mysql_prlock_unlock(&LOCK_system_variables_hash);

  if (options_only || state == PLUGIN_IS_DISABLED)
  {
    ret= !options_only && plugin_is_forced(plugin);
    state= PLUGIN_IS_DISABLED;
    goto err;
  }

  if (plugin_type_initialize[plugin->plugin->type])
  {
    if ((*plugin_type_initialize[plugin->plugin->type])(plugin))
    {
      sql_print_error("Plugin '%s' registration as a %s failed.",
                      plugin->name.str, plugin_type_names[plugin->plugin->type].str);
      goto err;
    }
  }
  else if (plugin->plugin->init)
  {
    if (plugin->plugin->init(plugin))
    {
      sql_print_error("Plugin '%s' init function returned error.",
                      plugin->name.str);
      goto err;
    }
  }
  state= PLUGIN_IS_READY; // plugin->init() succeeded

  if (plugin->plugin->status_vars)
  {
    /*
      historical ndb behavior caused MySQL plugins to specify
      status var names in full, with the plugin name prefix.
      this was never fixed in MySQL.
      MariaDB fixes that but supports MySQL style too.
    */
    SHOW_VAR *show_vars= plugin->plugin->status_vars;
    SHOW_VAR tmp_array[2]= {
      {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
      {0, 0, SHOW_UNDEF}
    };
    if (strncasecmp(show_vars->name, plugin->name.str, plugin->name.length))
      show_vars= tmp_array;

    if (add_status_vars(show_vars))
      goto err;
  }

  ret= 0;

err:
  if (ret)
    plugin_variables_deinit(plugin);

  mysql_mutex_lock(&LOCK_plugin);
  plugin->state= state;

  DBUG_RETURN(ret);
}


bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
                            const LEX_STRING *dl_arg)
{
  TABLE *table;
  TABLE_LIST tables;
  LEX_STRING dl= *dl_arg;
  bool error= false;
  unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
  { MYSQL_AUDIT_GENERAL_CLASSMASK };
  DBUG_ENTER("mysql_uninstall_plugin");

  tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);

  if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
    DBUG_RETURN(TRUE);

  WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)

  /* need to open before acquiring LOCK_plugin or it will deadlock */
  if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
    DBUG_RETURN(TRUE);

  if (!table->key_info)
  {
    my_printf_error(ER_UNKNOWN_ERROR,
                    "The table %s.%s has no primary key. "
                    "Please check the table definition and "
                    "create the primary key accordingly.", MYF(0),
                    table->s->db.str, table->s->table_name.str);
    DBUG_RETURN(TRUE);
  }

  /*
    Pre-acquire audit plugins for events that may potentially occur
    during [UN]INSTALL PLUGIN.

    When audit event is triggered, audit subsystem acquires interested
    plugins by walking through plugin list. Evidently plugin list
    iterator protects plugin list by acquiring LOCK_plugin, see
    plugin_foreach_with_mask().

    On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
    rather for a long time.

    When audit event is triggered during [UN]INSTALL PLUGIN, plugin
    list iterator acquires the same lock (within the same thread)
    second time.

    This hack should be removed when LOCK_plugin is fixed so it
    protects only what it supposed to protect.

    See also mysql_install_plugin() and initialize_audit_plugin()
  */
  if (mysql_audit_general_enabled())
    mysql_audit_acquire_plugins(thd, event_class_mask);

  mysql_mutex_lock(&LOCK_plugin);

  if (name->str)
    error= do_uninstall(thd, table, name);
  else
  {
    fix_dl_name(thd->mem_root, &dl);
    st_plugin_dl *plugin_dl= plugin_dl_find(&dl);
    if (plugin_dl)
    {
      for (struct st_maria_plugin *plugin= plugin_dl->plugins;
           plugin->info; plugin++)
      {
        LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
        error|= do_uninstall(thd, table, &str);
      }
    }
    else
    {
      my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SONAME", dl.str);
      error= true;
    }
  }
  reap_plugins();

  global_plugin_version++;
  mysql_mutex_unlock(&LOCK_plugin);
  DBUG_RETURN(error);

WSREP_ERROR_LABEL:
  DBUG_RETURN(TRUE);
}


my_bool mark_changed(int, const struct my_option *opt, char *)
{
  if (opt->app_type)
  {
    sys_var *var= (sys_var*) opt->app_type;
    var->value_origin= sys_var::CONFIG;
  }
  return 0;
}


sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *plugin_var)
{
  for (sys_var *var= plugin->system_vars; var; var= var->next)
  {
    sys_var_pluginvar *pvar=var->cast_pluginvar();
    if (pvar->plugin_var == plugin_var)
      return var;
  }
  return 0;
}


static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type)
{
  SHOW_COMP_OPTION rc= SHOW_OPTION_NO;
  struct st_plugin_int *plugin;
  DBUG_ENTER("plugin_is_ready");
  mysql_mutex_lock(&LOCK_plugin);
  if ((plugin= plugin_find_internal(name, type)))
  {
    rc= SHOW_OPTION_DISABLED;
    if (plugin->state == PLUGIN_IS_READY)
      rc= SHOW_OPTION_YES;
  }
  mysql_mutex_unlock(&LOCK_plugin);
  DBUG_RETURN(rc);
}


virtual void session_save_default(THD *thd, set_var *var) {}


bool sys_var_pluginvar::global_update(THD *thd, set_var *var)
{
  DBUG_ASSERT(!is_readonly());
  mysql_mutex_assert_owner(&LOCK_global_system_variables);

  void *tgt= real_value_ptr(thd, OPT_GLOBAL);
  const void *src= &var->save_result;

  if (!var->value)
    src= var_def_ptr(plugin_var);

  plugin_var->update(thd, plugin_var, tgt, src);
  return false;
}


  uchar *default_value_ptr(THD *thd)
  { return do_value_ptr(thd, OPT_DEFAULT, 0); }


  uchar* global_value_ptr(THD *thd, const LEX_STRING *base)
  { return do_value_ptr(thd, OPT_GLOBAL, base); }


static char **mysql_sys_var_str(THD* thd, int offset)
{
  return (char **) intern_sys_var_ptr(thd, offset, true);
}


static double *mysql_sys_var_double(THD* thd, int offset)
{
  return (double *) intern_sys_var_ptr(thd, offset, true);
}


int thd_setspecific(MYSQL_THD thd, MYSQL_THD_KEY_T key, void *value)
{
  DBUG_ASSERT(key != INVALID_THD_KEY);
  if (key == INVALID_THD_KEY || (!thd && !(thd= current_thd)))
    return EINVAL;

  memcpy(intern_sys_var_ptr(thd, key, true), &value, sizeof(void*));
  return 0;
}


bool plugin_is_ready(const LEX_STRING *name, int type)
{
  bool rc= FALSE;
  if (plugin_status(name, type) == SHOW_OPTION_YES)
    rc= TRUE;
  return rc;
}


static unsigned long *mysql_sys_var_ulong(THD* thd, int offset)
{
  return (unsigned long *) intern_sys_var_ptr(thd, offset, true);
}


static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int type)
{
  uint i;
  DBUG_ENTER("plugin_find_internal");
  if (! initialized)
    DBUG_RETURN(0);

  mysql_mutex_assert_owner(&LOCK_plugin);

  if (type == MYSQL_ANY_PLUGIN)
  {
    for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
    {
      struct st_plugin_int *plugin= (st_plugin_int *)
        my_hash_search(&plugin_hash[i], (const uchar *)name->str, name->length);
      if (plugin)
        DBUG_RETURN(plugin);
    }
  }
  else
    DBUG_RETURN((st_plugin_int *)
        my_hash_search(&plugin_hash[type], (const uchar *)name->str,
                       name->length));
  DBUG_RETURN(0);
}


static void free_plugin_mem(struct st_plugin_dl *p)
{
#ifdef HAVE_DLOPEN
  if (p->ptr_backup)
  {
    DBUG_ASSERT(p->nbackups);
    DBUG_ASSERT(p->handle);
    restore_ptr_backup(p->nbackups, p->ptr_backup);
    my_free(p->ptr_backup);
  }
  if (p->handle)
    dlclose(p->handle);
#endif
  my_free(p->dl.str);
  if (p->allocated)
    my_free(p->plugins);
}


static const void *var_def_ptr(st_mysql_sys_var *pv)
{
    switch (pv->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
    case PLUGIN_VAR_INT:
      return &((sysvar_uint_t*) pv)->def_val;
    case PLUGIN_VAR_LONG:
      return &((sysvar_ulong_t*) pv)->def_val;
    case PLUGIN_VAR_LONGLONG:
      return &((sysvar_ulonglong_t*) pv)->def_val;
    case PLUGIN_VAR_ENUM:
      return &((sysvar_enum_t*) pv)->def_val;
    case PLUGIN_VAR_SET:
      return &((sysvar_set_t*) pv)->def_val;
    case PLUGIN_VAR_BOOL:
      return &((sysvar_bool_t*) pv)->def_val;
    case PLUGIN_VAR_STR:
      return &((sysvar_str_t*) pv)->def_val;
    case PLUGIN_VAR_DOUBLE:
      return &((sysvar_double_t*) pv)->def_val;
    case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_uint_t*) pv)->def_val;
    case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_ulong_t*) pv)->def_val;
    case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_ulonglong_t*) pv)->def_val;
    case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_enum_t*) pv)->def_val;
    case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_set_t*) pv)->def_val;
    case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_bool_t*) pv)->def_val;
    case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_str_t*) pv)->def_val;
    case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
      return &((thdvar_double_t*) pv)->def_val;
    default:
      DBUG_ASSERT(0);
      return NULL;
    }
}


static SHOW_TYPE pluginvar_show_type(const st_mysql_sys_var *plugin_var)
{
  switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_UNSIGNED)) {
  case PLUGIN_VAR_BOOL:
    return SHOW_MY_BOOL;
  case PLUGIN_VAR_INT:
    return SHOW_SINT;
  case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED:
    return SHOW_UINT;
  case PLUGIN_VAR_LONG:
    return SHOW_SLONG;
  case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED:
    return SHOW_ULONG;
  case PLUGIN_VAR_LONGLONG:
    return SHOW_SLONGLONG;
  case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED:
    return SHOW_ULONGLONG;
  case PLUGIN_VAR_STR:
    return SHOW_CHAR_PTR;
  case PLUGIN_VAR_ENUM:
  case PLUGIN_VAR_SET:
    return SHOW_CHAR;
  case PLUGIN_VAR_DOUBLE:
    return SHOW_DOUBLE;
  default:
    DBUG_ASSERT(0);
    return SHOW_UNDEF;
  }
}


static int item_value_type(struct st_mysql_value *value)
{
  switch (((st_item_value_holder*)value)->item->result_type()) {
  case INT_RESULT:
    return MYSQL_VALUE_TYPE_INT;
  case REAL_RESULT:
    return MYSQL_VALUE_TYPE_REAL;
  default:
    return MYSQL_VALUE_TYPE_STRING;
  }
}


static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
                                      void *sym, char *dlpath,
                                      int report)
{
  DBUG_ENTER("read_maria_plugin_info");

  /* Determine interface version */
  if (!(sym))
  {
    /*
      Actually this branch impossible because in case of absence of maria
      version we try mysql version.
    */
    report_error(report, ER_CANT_FIND_DL_ENTRY,
                 maria_plugin_interface_version_sym);
    DBUG_RETURN(TRUE);
  }
  plugin_dl->mariaversion= *(int *)sym;
  plugin_dl->mysqlversion= 0;
  /* Versioning */
  if (plugin_dl->mariaversion < min_maria_plugin_interface_version ||
      (plugin_dl->mariaversion >> 8) > (MARIA_PLUGIN_INTERFACE_VERSION >> 8))
  {
    report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC,
                 "plugin interface version mismatch");
    DBUG_RETURN(TRUE);
  }
  /* Find plugin declarations */
  if (!(sym= dlsym(plugin_dl->handle, maria_plugin_declarations_sym)))
  {
    report_error(report, ER_CANT_FIND_DL_ENTRY, maria_plugin_declarations_sym);
    DBUG_RETURN(TRUE);
  }
  if (plugin_dl->mariaversion != MARIA_PLUGIN_INTERFACE_VERSION)
  {
    uint sizeof_st_plugin;
    struct st_maria_plugin *old, *cur;
    char *ptr= (char *)sym;

    if ((sym= dlsym(plugin_dl->handle, maria_sizeof_st_plugin_sym)))
      sizeof_st_plugin= *(int *)sym;
    else
    {
      report_error(report, ER_CANT_FIND_DL_ENTRY, maria_sizeof_st_plugin_sym);
      DBUG_RETURN(TRUE);
    }

    if (sizeof_st_plugin != sizeof(st_mysql_plugin))
    {
      int i;
      for (i= 0;
           ((struct st_maria_plugin *)(ptr + i * sizeof_st_plugin))->info;
           i++)
        /* no op */;

      cur= (struct st_maria_plugin*)
        my_malloc((i + 1) * sizeof(struct st_maria_plugin),
                  MYF(MY_ZEROFILL|MY_WME));
      if (!cur)
      {
        report_error(report, ER_OUTOFMEMORY,
                     static_cast<int>(plugin_dl->dl.length));
        DBUG_RETURN(TRUE);
      }
      /*
        All st_plugin fields not initialized in the plugin explicitly, are
        set to 0. It matches C standard behaviour for struct initializers that
        have less values than the struct definition.
      */
      for (i=0;
           (old= (struct st_maria_plugin *)(ptr + i * sizeof_st_plugin))->info;
           i++)
        memcpy(cur + i, old, MY_MIN(sizeof(cur[i]), sizeof_st_plugin));

      sym= cur;
      plugin_dl->allocated= true;
    }
    else
      sym= ptr;
  }
  plugin_dl->plugins= (struct st_maria_plugin *)sym;

  DBUG_RETURN(FALSE);
}


static int item_val_real(struct st_mysql_value *value, double *buf)
{
  Item *item= ((st_item_value_holder*)value)->item;
  *buf= item->val_real();
  if (item->is_null())
    return 1;
  return 0;
}


int plugin_init(int *argc, char **argv, int flags)
{
  uint i;
  struct st_maria_plugin **builtins;
  struct st_maria_plugin *plugin;
  struct st_plugin_int tmp, *plugin_ptr, **reap;
  MEM_ROOT tmp_root;
  bool reaped_mandatory_plugin= false;
  bool mandatory= true;
  LEX_STRING MyISAM= { C_STRING_WITH_LEN("MyISAM") };
  DBUG_ENTER("plugin_init");

  if (initialized)
    DBUG_RETURN(0);

  dlopen_count =0;

  init_alloc_root(&plugin_mem_root, 4096, 4096, MYF(0));
  init_alloc_root(&plugin_vars_mem_root, 4096, 4096, MYF(0));
  init_alloc_root(&tmp_root, 4096, 4096, MYF(0));

  if (my_hash_init(&bookmark_hash, &my_charset_bin, 32, 0, 0,
                   get_bookmark_hash_key, NULL, HASH_UNIQUE))
      goto err;

  /*
    The 80 is from 2016-04-27 when we had 71 default plugins
    Big enough to avoid many mallocs even in future
  */
  if (my_init_dynamic_array(&plugin_dl_array,
                            sizeof(struct st_plugin_dl *), 16, 16, MYF(0)) ||
      my_init_dynamic_array(&plugin_array,
                            sizeof(struct st_plugin_int *), 80, 32, MYF(0)))
    goto err;

  for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
  {
    if (my_hash_init(&plugin_hash[i], system_charset_info, 32, 0, 0,
                     get_plugin_hash_key, NULL, HASH_UNIQUE))
      goto err;
  }

  /* prepare debug_sync service */
  DBUG_ASSERT(strcmp(list_of_services[1].name, "debug_sync_service") == 0);
  list_of_services[1].service= *(void**)&debug_sync_C_callback_ptr;

  /* prepare encryption_keys service */
  finalize_encryption_plugin(0);

  mysql_mutex_lock(&LOCK_plugin);

  initialized= 1;

  /*
    First we register builtin plugins
  */
  if (global_system_variables.log_warnings >= 9)
    sql_print_information("Initializing built-in plugins");

  for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)
  {
    if (!*builtins)
    {
      builtins= mysql_optional_plugins;
      mandatory= false;
      if (!*builtins)
        break;
    }
    for (plugin= *builtins; plugin->info; plugin++)
    {
      if (opt_ignore_builtin_innodb &&
          !my_strnncoll(&my_charset_latin1, (const uchar*) plugin->name,
                        6, (const uchar*) "InnoDB", 6))
        continue;

      bzero(&tmp, sizeof(tmp));
      tmp.plugin= plugin;
      tmp.name.str= (char *)plugin->name;
      tmp.name.length= strlen(plugin->name);
      tmp.state= 0;
      tmp.load_option= mandatory ? PLUGIN_FORCE : PLUGIN_ON;

      for (i=0; i < array_elements(override_plugin_load_policy); i++)
      {
        if (!my_strcasecmp(&my_charset_latin1, plugin->name,
                           override_plugin_load_policy[i].plugin_name))
        {
          tmp.load_option= override_plugin_load_policy[i].override;
          break;
        }
      }

      free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE));
      tmp.state= PLUGIN_IS_UNINITIALIZED;
      if (register_builtin(plugin, &tmp, &plugin_ptr))
        goto err_unlock;
    }
  }

  /*
    First, we initialize only MyISAM - that should almost always succeed
    (almost always, because plugins can be loaded outside of the server, too).
  */
  plugin_ptr= plugin_find_internal(&MyISAM, MYSQL_STORAGE_ENGINE_PLUGIN);
  DBUG_ASSERT(plugin_ptr || !mysql_mandatory_plugins[0]);
  if (plugin_ptr)
  {
    DBUG_ASSERT(plugin_ptr->load_option == PLUGIN_FORCE);

    if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv, false))
      goto err_unlock;

    /*
      set the global default storage engine variable so that it will
      not be null in any child thread.
    */
    global_system_variables.table_plugin =
      intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr));
      DBUG_ASSERT(plugin_ptr->ref_count == 1);

  }
  mysql_mutex_unlock(&LOCK_plugin);

  /* Register (not initialize!) all dynamic plugins */
  if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING))
  {
    I_List_iterator<i_string> iter(opt_plugin_load_list);
    i_string *item;
    if (global_system_variables.log_warnings >= 9)
      sql_print_information("Initializing plugins specified on the command line");
    while (NULL != (item= iter++))
      plugin_load_list(&tmp_root, item->ptr);

    if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
    {
      char path[FN_REFLEN + 1];
      build_table_filename(path, sizeof(path) - 1, "mysql", "plugin", reg_ext, 0);
      char engine_name_buf[NAME_CHAR_LEN + 1];
      LEX_STRING maybe_myisam= { engine_name_buf, 0 };
      frm_type_enum frm_type= dd_frm_type(NULL, path, &maybe_myisam);
      /* if mysql.plugin table is MyISAM - load it right away */
      if (frm_type == FRMTYPE_TABLE && !strcasecmp(maybe_myisam.str, "MyISAM"))
      {
        plugin_load(&tmp_root);
        flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE;
      }
    }
  }

  /*
    Now we initialize all remaining plugins
  */

  mysql_mutex_lock(&LOCK_plugin);
  reap= (st_plugin_int **) my_alloca((plugin_array.elements+1) * sizeof(void*));
  *(reap++)= NULL;

  for(;;)
  {
    for (i=0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
    {
      HASH *hash= plugin_hash + plugin_type_initialization_order[i];
      for (uint idx= 0; idx < hash->records; idx++)
      {
        plugin_ptr= (struct st_plugin_int *) my_hash_element(hash, idx);
        if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED)
        {
          if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv,
                                (flags & PLUGIN_INIT_SKIP_INITIALIZATION)))
          {
            plugin_ptr->state= PLUGIN_IS_DYING;
            *(reap++)= plugin_ptr;
          }
        }
      }
    }

    /* load and init plugins from the plugin table (unless done already) */
    if (flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE)
      break;

    mysql_mutex_unlock(&LOCK_plugin);
    plugin_load(&tmp_root);
    flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE;
    mysql_mutex_lock(&LOCK_plugin);
  }

  /*
    Check if any plugins have to be reaped
  */
  while ((plugin_ptr= *(--reap)))
  {
    mysql_mutex_unlock(&LOCK_plugin);
    if (plugin_is_forced(plugin_ptr))
      reaped_mandatory_plugin= TRUE;
    plugin_deinitialize(plugin_ptr, true);
    mysql_mutex_lock(&LOCK_plugin);
    plugin_del(plugin_ptr);
  }

  mysql_mutex_unlock(&LOCK_plugin);
  my_afree(reap);
  if (reaped_mandatory_plugin)
    goto err;

  free_root(&tmp_root, MYF(0));

  DBUG_RETURN(0);

err_unlock:
  mysql_mutex_unlock(&LOCK_plugin);
err:
  free_root(&tmp_root, MYF(0));
  DBUG_RETURN(1);
}


sys_var_pluginvar::sys_var_pluginvar(sys_var_chain *chain, const char *name_arg,
        st_plugin_int *p, st_mysql_sys_var *pv)
    : sys_var(chain, name_arg, pv->comment, pluginvar_sysvar_flags(pv),
              0, pv->flags & PLUGIN_VAR_NOCMDOPT ? -1 : 0, NO_ARG,
              pluginvar_show_type(pv), 0,
              NULL, VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL),
    plugin(p), plugin_var(pv)
{
  plugin_var->name= name_arg;
  plugin_opt_set_limits(&option, pv);
}


static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
{
  int i;
  st_plugin_int *pi;
  DBUG_ENTER("intern_plugin_unlock");

  mysql_mutex_assert_owner(&LOCK_plugin);

  if (!plugin)
    DBUG_VOID_RETURN;

  pi= plugin_ref_to_int(plugin);

#ifdef DBUG_OFF
  if (!pi->plugin_dl)
    DBUG_VOID_RETURN;
#else
  my_free(plugin);
#endif

  if (lex)
  {
    /*
      Remove one instance of this plugin from the use list.
      We are searching backwards so that plugins locked last
      could be unlocked faster - optimizing for LIFO semantics.
    */
    for (i= lex->plugins.elements - 1; i >= 0; i--)
      if (plugin == *dynamic_element(&lex->plugins, i, plugin_ref*))
      {
        delete_dynamic_element(&lex->plugins, i);
        break;
      }
    DBUG_ASSERT(i >= 0);
  }

  DBUG_ASSERT(pi->ref_count);
  pi->ref_count--;

  DBUG_PRINT("lock",("thd: %p  plugin: \"%s\" UNLOCK ref_count: %d",
                     current_thd, pi->name.str, pi->ref_count));

  if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count)
    reap_needed= true;

  DBUG_VOID_RETURN;
}


static st_bookmark *find_bookmark(const char *plugin, const char *name,
                                  int flags)
{
  st_bookmark *result= NULL;
  uint namelen, length, pluginlen= 0;
  char *varname, *p;

  if (!(flags & PLUGIN_VAR_THDLOCAL))
    return NULL;

  namelen= strlen(name);
  if (plugin)
    pluginlen= strlen(plugin) + 1;
  length= namelen + pluginlen + 2;
  varname= (char*) my_alloca(length);

  if (plugin)
  {
    strxmov(varname + 1, plugin, "_", name, NullS);
    for (p= varname + 1; *p; p++)
      if (*p == '-')
        *p= '_';
  }
  else
    memcpy(varname + 1, name, namelen + 1);

  varname[0]= plugin_var_bookmark_key(flags);

  result= (st_bookmark*) my_hash_search(&bookmark_hash,
                                        (const uchar*) varname, length - 1);

  my_afree(varname);
  return result;
}


static size_t var_storage_size(int flags)
{
  switch (flags & PLUGIN_VAR_TYPEMASK) {
  case PLUGIN_VAR_BOOL:         return sizeof(my_bool);
  case PLUGIN_VAR_INT:          return sizeof(int);
  case PLUGIN_VAR_LONG:         return sizeof(long);
  case PLUGIN_VAR_ENUM:         return sizeof(long);
  case PLUGIN_VAR_LONGLONG:     return sizeof(ulonglong);
  case PLUGIN_VAR_SET:          return sizeof(ulonglong);
  case PLUGIN_VAR_STR:          return sizeof(char*);
  case PLUGIN_VAR_DOUBLE:       return sizeof(double);
  default: DBUG_ASSERT(0);      return 0;
  }
}


void plugin_opt_set_limits(struct my_option *options,
                           const struct st_mysql_sys_var *opt)
{
  options->sub_size= 0;

  switch (opt->flags & (PLUGIN_VAR_TYPEMASK |
                        PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL)) {
  /* global system variables */
  case PLUGIN_VAR_INT:
    OPTION_SET_LIMITS(GET_INT, options, (sysvar_int_t*) opt);
    break;
  case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED:
    OPTION_SET_LIMITS(GET_UINT, options, (sysvar_uint_t*) opt);
    break;
  case PLUGIN_VAR_LONG:
    OPTION_SET_LIMITS(GET_LONG, options, (sysvar_long_t*) opt);
    break;
  case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED:
    OPTION_SET_LIMITS(GET_ULONG, options, (sysvar_ulong_t*) opt);
    break;
  case PLUGIN_VAR_LONGLONG:
    OPTION_SET_LIMITS(GET_LL, options, (sysvar_longlong_t*) opt);
    break;
  case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED:
    OPTION_SET_LIMITS(GET_ULL, options, (sysvar_ulonglong_t*) opt);
    break;
  case PLUGIN_VAR_ENUM:
    options->var_type= GET_ENUM;
    options->typelib= ((sysvar_enum_t*) opt)->typelib;
    options->def_value= ((sysvar_enum_t*) opt)->def_val;
    options->min_value= options->block_size= 0;
    options->max_value= options->typelib->count - 1;
    break;
  case PLUGIN_VAR_SET:
    options->var_type= GET_SET;
    options->typelib= ((sysvar_set_t*) opt)->typelib;
    options->def_value= ((sysvar_set_t*) opt)->def_val;
    options->min_value= options->block_size= 0;
    options->max_value= (1ULL << options->typelib->count) - 1;
    break;
  case PLUGIN_VAR_BOOL:
    options->var_type= GET_BOOL;
    options->def_value= ((sysvar_bool_t*) opt)->def_val;
    options->typelib= &bool_typelib;
    break;
  case PLUGIN_VAR_STR:
    options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
                        GET_STR_ALLOC : GET_STR);
    options->def_value= (intptr) ((sysvar_str_t*) opt)->def_val;
    break;
  case PLUGIN_VAR_DOUBLE:
    OPTION_SET_LIMITS_DOUBLE(options, (sysvar_double_t*) opt);
    break;
  /* threadlocal variables */
  case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
    OPTION_SET_LIMITS(GET_INT, options, (thdvar_int_t*) opt);
    break;
  case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
    OPTION_SET_LIMITS(GET_UINT, options, (thdvar_uint_t*) opt);
    break;
  case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
    OPTION_SET_LIMITS(GET_LONG, options, (thdvar_long_t*) opt);
    break;
  case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
    OPTION_SET_LIMITS(GET_ULONG, options, (thdvar_ulong_t*) opt);
    break;
  case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
    OPTION_SET_LIMITS(GET_LL, options, (thdvar_longlong_t*) opt);
    break;
  case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
    OPTION_SET_LIMITS(GET_ULL, options, (thdvar_ulonglong_t*) opt);
    break;
  case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
    OPTION_SET_LIMITS_DOUBLE(options, (thdvar_double_t*) opt);
    break;
  case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
    options->var_type= GET_ENUM;
    options->typelib= ((thdvar_enum_t*) opt)->typelib;
    options->def_value= ((thdvar_enum_t*) opt)->def_val;
    options->min_value= options->block_size= 0;
    options->max_value= options->typelib->count - 1;
    break;
  case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
    options->var_type= GET_SET;
    options->typelib= ((thdvar_set_t*) opt)->typelib;
    options->def_value= ((thdvar_set_t*) opt)->def_val;
    options->min_value= options->block_size= 0;
    options->max_value= (1ULL << options->typelib->count) - 1;
    break;
  case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
    options->var_type= GET_BOOL;
    options->def_value= ((thdvar_bool_t*) opt)->def_val;
    options->typelib= &bool_typelib;
    break;
  case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
    options->var_type= ((opt->flags & PLUGIN_VAR_MEMALLOC) ?
                        GET_STR_ALLOC : GET_STR);
    options->def_value= (intptr) ((thdvar_str_t*) opt)->def_val;
    break;
  default:
    DBUG_ASSERT(0);
  }
  options->arg_type= REQUIRED_ARG;
  if (opt->flags & PLUGIN_VAR_NOCMDARG)
    options->arg_type= NO_ARG;
  if (opt->flags & PLUGIN_VAR_OPCMDARG)
    options->arg_type= OPT_ARG;
}


bool sys_var_pluginvar::do_check(THD *thd, set_var *var)
{
  st_item_value_holder value;
  DBUG_ASSERT(!is_readonly());
  DBUG_ASSERT(plugin_var->check);

  value.value_type= item_value_type;
  value.val_str= item_val_str;
  value.val_int= item_val_int;
  value.val_real= item_val_real;
  value.is_unsigned= item_is_unsigned;
  value.item= var->value;

  return plugin_var->check(thd, plugin_var, &var->save_result, &value);
}


static void cleanup_variables(struct system_variables *vars)
{
  st_bookmark *v;
  uint idx;

  mysql_prlock_rdlock(&LOCK_system_variables_hash);
  for (idx= 0; idx < bookmark_hash.records; idx++)
  {
    v= (st_bookmark*) my_hash_element(&bookmark_hash, idx);

    if (v->version > vars->dynamic_variables_version)
      continue; /* not in vars */

    DBUG_ASSERT((uint)v->offset <= vars->dynamic_variables_head);

    /* free allocated strings (PLUGIN_VAR_STR | PLUGIN_VAR_MEMALLOC) */
    if ((v->key[0] & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
         v->key[0] & BOOKMARK_MEMALLOC)
    {
      char **ptr= (char**)(vars->dynamic_variables_ptr + v->offset);
      my_free(*ptr);
      *ptr= NULL;
    }
  }
  mysql_prlock_unlock(&LOCK_system_variables_hash);

  DBUG_ASSERT(vars->table_plugin == NULL);
  DBUG_ASSERT(vars->tmp_table_plugin == NULL);
  DBUG_ASSERT(vars->enforced_table_plugin == NULL);

  my_free(vars->dynamic_variables_ptr);
  vars->dynamic_variables_ptr= NULL;
  vars->dynamic_variables_size= 0;
  vars->dynamic_variables_version= 0;
}


static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var,
                               void *save, st_mysql_value *value)
{
  my_bool fixed1, fixed2;
  long long orig, val;
  struct my_option options;
  value->val_int(value, &orig);
  val= orig;
  plugin_opt_set_limits(&options, var);

  if (var->flags & PLUGIN_VAR_UNSIGNED)
  {
    if ((fixed1= (!value->is_unsigned(value) && val < 0)))
      val=0;
    *(ulonglong *)save= getopt_ull_limit_value((ulonglong) val, &options,
                                               &fixed2);
  }
  else
  {
    if ((fixed1= (value->is_unsigned(value) && val < 0)))
      val=LONGLONG_MAX;
    *(longlong *)save= getopt_ll_limit_value(val, &options, &fixed2);
  }

  return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
                              value->is_unsigned(value), (longlong) orig);
}


static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
                             my_option *options)
{
  const char *plugin_name= tmp->plugin->name;
  const LEX_STRING plugin_dash = { C_STRING_WITH_LEN("plugin-") };
  uint plugin_name_len= strlen(plugin_name);
  uint optnamelen;
  const int max_comment_len= 255;
  char *comment= (char *) alloc_root(mem_root, max_comment_len + 1);
  char *optname;

  int index= 0, UNINIT_VAR(offset);
  st_mysql_sys_var *opt, **plugin_option;
  st_bookmark *v;

  /** Used to circumvent the const attribute on my_option::name */
  char *plugin_name_ptr, *plugin_name_with_prefix_ptr;

  DBUG_ENTER("construct_options");

  plugin_name_ptr= (char*) alloc_root(mem_root, plugin_name_len + 1);
  strcpy(plugin_name_ptr, plugin_name);
  my_casedn_str(&my_charset_latin1, plugin_name_ptr);
  convert_underscore_to_dash(plugin_name_ptr, plugin_name_len);
  plugin_name_with_prefix_ptr= (char*) alloc_root(mem_root,
                                                  plugin_name_len +
                                                  plugin_dash.length + 1);
  strxmov(plugin_name_with_prefix_ptr, plugin_dash.str, plugin_name_ptr, NullS);

  if (!plugin_is_forced(tmp))
  {
    /* support --skip-plugin-foo syntax */
    options[0].name= plugin_name_ptr;
    options[1].name= plugin_name_with_prefix_ptr;
    options[0].id= options[1].id= 0;
    options[0].var_type= options[1].var_type= GET_ENUM;
    options[0].arg_type= options[1].arg_type= OPT_ARG;
    options[0].def_value= options[1].def_value= 1; /* ON */
    options[0].typelib= options[1].typelib= &global_plugin_typelib;

    strxnmov(comment, max_comment_len, "Enable or disable ", plugin_name,
            " plugin. One of: ON, OFF, FORCE (don't start if the plugin"
            " fails to load), FORCE_PLUS_PERMANENT (like FORCE, but the"
            " plugin can not be uninstalled).", NullS);
    options[0].comment= comment;
    /*
      Allocate temporary space for the value of the tristate.
      This option will have a limited lifetime and is not used beyond
      server initialization.
      GET_ENUM value is an unsigned long integer.
    */
    options[0].value= options[1].value=
                      (uchar **)alloc_root(mem_root, sizeof(ulong));
    *((ulong*) options[0].value)= (ulong) options[0].def_value;

    options+= 2;
  }

  /*
    Two passes as the 2nd pass will take pointer addresses for use
    by my_getopt and register_var() in the first pass uses realloc
  */

  for (plugin_option= tmp->plugin->system_vars;
       plugin_option && *plugin_option; plugin_option++, index++)
  {
    opt= *plugin_option;

    if (!opt->name)
    {
      sql_print_error("Missing variable name in plugin '%s'.",
                      plugin_name);
      DBUG_RETURN(-1);
    }

    if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
      continue;
    if (!(register_var(plugin_name_ptr, opt->name, opt->flags)))
      continue;
    switch (opt->flags & PLUGIN_VAR_TYPEMASK) {
    case PLUGIN_VAR_BOOL:
      ((thdvar_bool_t *) opt)->resolve= mysql_sys_var_char;
      break;
    case PLUGIN_VAR_INT:
      ((thdvar_int_t *) opt)->resolve= mysql_sys_var_int;
      break;
    case PLUGIN_VAR_LONG:
      ((thdvar_long_t *) opt)->resolve= mysql_sys_var_long;
      break;
    case PLUGIN_VAR_LONGLONG:
      ((thdvar_longlong_t *) opt)->resolve= mysql_sys_var_longlong;
      break;
    case PLUGIN_VAR_STR:
      ((thdvar_str_t *) opt)->resolve= mysql_sys_var_str;
      break;
    case PLUGIN_VAR_ENUM:
      ((thdvar_enum_t *) opt)->resolve= mysql_sys_var_ulong;
      break;
    case PLUGIN_VAR_SET:
      ((thdvar_set_t *) opt)->resolve= mysql_sys_var_ulonglong;
      break;
    case PLUGIN_VAR_DOUBLE:
      ((thdvar_double_t *) opt)->resolve= mysql_sys_var_double;
      break;
    default:
      sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
                      opt->flags, plugin_name);
      DBUG_RETURN(-1);
    };
  }

  for (plugin_option= tmp->plugin->system_vars;
       plugin_option && *plugin_option; plugin_option++, index++)
  {
    switch ((opt= *plugin_option)->flags & PLUGIN_VAR_TYPEMASK) {
    case PLUGIN_VAR_BOOL:
      if (!opt->check)
        opt->check= check_func_bool;
      if (!opt->update)
        opt->update= update_func_bool;
      break;
    case PLUGIN_VAR_INT:
      if (!opt->check)
        opt->check= check_func_int;
      if (!opt->update)
        opt->update= update_func_int;
      break;
    case PLUGIN_VAR_LONG:
      if (!opt->check)
        opt->check= check_func_long;
      if (!opt->update)
        opt->update= update_func_long;
      break;
    case PLUGIN_VAR_LONGLONG:
      if (!opt->check)
        opt->check= check_func_longlong;
      if (!opt->update)
        opt->update= update_func_longlong;
      break;
    case PLUGIN_VAR_STR:
      if (!opt->check)
        opt->check= check_func_str;
      if (!opt->update)
      {
        opt->update= update_func_str;
        if (!(opt->flags & (PLUGIN_VAR_MEMALLOC | PLUGIN_VAR_READONLY)))
        {
          opt->flags|= PLUGIN_VAR_READONLY;
          sql_print_warning("Server variable %s of plugin %s was forced "
                            "to be read-only: string variable without "
                            "update_func and PLUGIN_VAR_MEMALLOC flag",
                            opt->name, plugin_name);
        }
      }
      break;
    case PLUGIN_VAR_ENUM:
      if (!opt->check)
        opt->check= check_func_enum;
      if (!opt->update)
        opt->update= update_func_long;
      break;
    case PLUGIN_VAR_SET:
      if (!opt->check)
        opt->check= check_func_set;
      if (!opt->update)
        opt->update= update_func_longlong;
      break;
    case PLUGIN_VAR_DOUBLE:
      if (!opt->check)
        opt->check= check_func_double;
      if (!opt->update)
        opt->update= update_func_double;
      break;
    default:
      sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
                      opt->flags, plugin_name);
      DBUG_RETURN(-1);
    }

    if ((opt->flags & (PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_THDLOCAL))
                    == PLUGIN_VAR_NOCMDOPT)
      continue;

    if (!(opt->flags & PLUGIN_VAR_THDLOCAL))
    {
      optnamelen= strlen(opt->name);
      optname= (char*) alloc_root(mem_root, plugin_name_len + optnamelen + 2);
      strxmov(optname, plugin_name_ptr, "-", opt->name, NullS);
      optnamelen= plugin_name_len + optnamelen + 1;
    }
    else
    {
      /* this should not fail because register_var should create entry */
      if (!(v= find_bookmark(plugin_name_ptr, opt->name, opt->flags)))
      {
        sql_print_error("Thread local variable '%s' not allocated "
                        "in plugin '%s'.", opt->name, plugin_name);
        DBUG_RETURN(-1);
      }

      *(int*)(opt + 1)= offset= v->offset;

      if (opt->flags & PLUGIN_VAR_NOCMDOPT)
      {
        char *val= global_system_variables.dynamic_variables_ptr + offset;
        if (((opt->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR) &&
             (opt->flags & PLUGIN_VAR_MEMALLOC))
        {
          char *def_val= *(char**)var_def_ptr(opt);
          *(char**)val= def_val ? my_strdup(def_val, MYF(0)) : NULL;
        }
        else
          memcpy(val, var_def_ptr(opt), var_storage_size(opt->flags));
        continue;
      }

      optname= (char*) memdup_root(mem_root, v->key + 1,
                                   (optnamelen= v->name_len) + 1);
    }

    convert_underscore_to_dash(optname, optnamelen);

    options->name= optname;
    options->comment= opt->comment;
    options->app_type= (opt->flags & PLUGIN_VAR_NOSYSVAR) ? NULL : opt;
    options->id= 0;

    plugin_opt_set_limits(options, opt);

    if (opt->flags & PLUGIN_VAR_THDLOCAL)
      options->value= options->u_max_value= (uchar**)
        (global_system_variables.dynamic_variables_ptr + offset);
    else
      options->value= options->u_max_value= *(uchar***) (opt + 1);

    char *option_name_ptr;
    options[1]= options[0];
    options[1].name= option_name_ptr= (char*) alloc_root(mem_root,
                                                        plugin_dash.length +
                                                        optnamelen + 1);
    options[1].comment= 0; /* Hidden from the help text */
    strxmov(option_name_ptr, plugin_dash.str, optname, NullS);

    options+= 2;
  }

  DBUG_RETURN(0);
}


static int pluginvar_sysvar_flags(const st_mysql_sys_var *p)
{
  return (p->flags & PLUGIN_VAR_THDLOCAL ? sys_var::SESSION : sys_var::GLOBAL)
       | (p->flags & PLUGIN_VAR_READONLY ? sys_var::READONLY : 0);
}


static char *mysql_sys_var_char(THD* thd, int offset)
{
  return (char *) intern_sys_var_ptr(thd, offset, true);
}


static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
{
#ifdef HAVE_DLOPEN
  char dlpath[FN_REFLEN];
  uint plugin_dir_len, dummy_errors, i;
  struct st_plugin_dl *tmp= 0, plugin_dl;
  void *sym;
  st_ptr_backup tmp_backup[array_elements(list_of_services)];
  DBUG_ENTER("plugin_dl_add");
  DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
                       dl->str, (int) dl->length));
  mysql_mutex_assert_owner(&LOCK_plugin);
  plugin_dir_len= strlen(opt_plugin_dir);
  /*
    Ensure that the dll doesn't have a path.
    This is done to ensure that only approved libraries from the
    plugin directory are used (to make this even remotely secure).
  */
  if (check_string_char_length((LEX_STRING *) dl, 0, NAME_CHAR_LEN,
                               system_charset_info, 1) ||
      check_valid_path(dl->str, dl->length) ||
      plugin_dir_len + dl->length + 1 >= FN_REFLEN)
  {
    report_error(report, ER_UDF_NO_PATHS);
    DBUG_RETURN(0);
  }
  /* If this dll is already loaded just increase ref_count. */
  if ((tmp= plugin_dl_find(dl)))
  {
    tmp->ref_count++;
    DBUG_RETURN(tmp);
  }
  bzero(&plugin_dl, sizeof(plugin_dl));
  /* Compile dll path */
  strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", dl->str, NullS);
  (void) unpack_filename(dlpath, dlpath);
  plugin_dl.ref_count= 1;
  /* Open new dll handle */
  if (!(plugin_dl.handle= dlopen(dlpath, RTLD_NOW)))
  {
    report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, my_dlerror(dlpath));
    goto ret;
  }
  dlopen_count++;

#ifdef HAVE_LINK_H
  if (global_system_variables.log_warnings > 2)
  {
    struct link_map *lm = (struct link_map*) plugin_dl.handle;
    sql_print_information("Loaded '%s' with offset 0x%zx", dl->str, (size_t)lm->l_addr);
  }
#endif

  /* Checks which plugin interface present and reads info */
  if (!(sym= dlsym(plugin_dl.handle, maria_plugin_interface_version_sym)))
  {
    if (read_mysql_plugin_info(&plugin_dl,
                               dlsym(plugin_dl.handle,
                                     plugin_interface_version_sym),
                               dlpath,
                               report))
      goto ret;
  }
  else
  {
    if (read_maria_plugin_info(&plugin_dl, sym, dlpath, report))
      goto ret;
  }

  /* link the services in */
  for (i= 0; i < array_elements(list_of_services); i++)
  {
    if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
    {
      void **ptr= (void **)sym;
      uint ver= (uint)(intptr)*ptr;
      if (ver > list_of_services[i].version ||
        (ver >> 8) < (list_of_services[i].version >> 8))
      {
        char buf[MYSQL_ERRMSG_SIZE];
        my_snprintf(buf, sizeof(buf),
                    "service '%s' interface version mismatch",
                    list_of_services[i].name);
        report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
        goto ret;
      }
      tmp_backup[plugin_dl.nbackups++].save(ptr);
      *ptr= list_of_services[i].service;
    }
  }

  if (plugin_dl.nbackups)
  {
    size_t bytes= plugin_dl.nbackups * sizeof(plugin_dl.ptr_backup[0]);
    plugin_dl.ptr_backup= (st_ptr_backup *)my_malloc(bytes, MYF(0));
    if (!plugin_dl.ptr_backup)
    {
      restore_ptr_backup(plugin_dl.nbackups, tmp_backup);
      report_error(report, ER_OUTOFMEMORY, bytes);
      goto ret;
    }
    memcpy(plugin_dl.ptr_backup, tmp_backup, bytes);
  }

  /* Duplicate and convert dll name */
  plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1;
  if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
  {
    report_error(report, ER_OUTOFMEMORY,
                 static_cast<int>(plugin_dl.dl.length));
    goto ret;
  }
  plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
    files_charset_info, dl->str, dl->length, system_charset_info,
    &dummy_errors);
  plugin_dl.dl.str[plugin_dl.dl.length]= 0;
  /* Add this dll to array */
  if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
  {
    report_error(report, ER_OUTOFMEMORY,
                 static_cast<int>(sizeof(struct st_plugin_dl)));
    goto ret;
  }

ret:
  if (!tmp)
    free_plugin_mem(&plugin_dl);

  DBUG_RETURN(tmp);

#else
  DBUG_ENTER("plugin_dl_add");
  report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN");
  DBUG_RETURN(0);
#endif
}


static void reap_plugins(void)
{
  uint count;
  struct st_plugin_int *plugin, **reap, **list;

  mysql_mutex_assert_owner(&LOCK_plugin);

  if (!reap_needed)
    return;

  reap_needed= false;
  count= plugin_array.elements;
  reap= (struct st_plugin_int **)my_alloca(sizeof(plugin)*(count+1));
  *(reap++)= NULL;

  for (uint i=0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
  {
    HASH *hash= plugin_hash + plugin_type_initialization_order[i];
    for (uint j= 0; j < hash->records; j++)
    {
      plugin= (struct st_plugin_int *) my_hash_element(hash, j);
      if (plugin->state == PLUGIN_IS_DELETED && !plugin->ref_count)
      {
        /* change the status flag to prevent reaping by another thread */
        plugin->state= PLUGIN_IS_DYING;
        *(reap++)= plugin;
      }
    }
  }

  mysql_mutex_unlock(&LOCK_plugin);

  list= reap;
  while ((plugin= *(--list)))
      plugin_deinitialize(plugin, true);

  mysql_mutex_lock(&LOCK_plugin);

  while ((plugin= *(--reap)))
    plugin_del(plugin);

  my_afree(reap);
}


static const char *item_val_str(struct st_mysql_value *value,
                                char *buffer, int *length)
{
  String str(buffer, *length, system_charset_info), *res;
  if (!(res= ((st_item_value_holder*)value)->item->val_str(&str)))
    return NULL;
  *length= res->length();
  if (res->c_ptr_quick() == buffer)
    return buffer;

  /*
    Lets be nice and create a temporary string since the
    buffer was too small
  */
  return current_thd->strmake(res->ptr(), res->length());
}


TYPELIB* sys_var_pluginvar::plugin_var_typelib(void)
{
  switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
  case PLUGIN_VAR_ENUM:
    return ((sysvar_enum_t *)plugin_var)->typelib;
  case PLUGIN_VAR_SET:
    return ((sysvar_set_t *)plugin_var)->typelib;
  case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
    return ((thdvar_enum_t *)plugin_var)->typelib;
  case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
    return ((thdvar_set_t *)plugin_var)->typelib;
  default:
    return NULL;
  }
  return NULL;	/* Keep compiler happy */
}


static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
                                      void *sym, char *dlpath,
                                      int report)
{
  DBUG_ENTER("read_maria_plugin_info");
  /* Determine interface version */
  if (!sym)
  {
    report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym);
    DBUG_RETURN(TRUE);
  }
  plugin_dl->mariaversion= 0;
  plugin_dl->mysqlversion= *(int *)sym;
  /* Versioning */
  if (plugin_dl->mysqlversion < min_plugin_interface_version ||
      (plugin_dl->mysqlversion >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8))
  {
    report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC,
                 "plugin interface version mismatch");
    DBUG_RETURN(TRUE);
  }
  /* Find plugin declarations */
  if (!(sym= dlsym(plugin_dl->handle, plugin_declarations_sym)))
  {
    report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym);
    DBUG_RETURN(TRUE);
  }

  /* convert mysql declaration to maria one */
  {
    int i;
    uint sizeof_st_plugin;
    struct st_mysql_plugin *old;
    struct st_maria_plugin *cur;
    char *ptr= (char *)sym;

    if ((sym= dlsym(plugin_dl->handle, sizeof_st_plugin_sym)))
      sizeof_st_plugin= *(int *)sym;
    else
    {
      DBUG_ASSERT(min_plugin_interface_version == 0);
      sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version);
    }

    for (i= 0;
         ((struct st_mysql_plugin *)(ptr + i * sizeof_st_plugin))->info;
         i++)
      /* no op */;

    cur= (struct st_maria_plugin*)
          my_malloc((i + 1) * sizeof(struct st_maria_plugin),
                    MYF(MY_ZEROFILL|MY_WME));
    if (!cur)
    {
      report_error(report, ER_OUTOFMEMORY,
                   static_cast<int>(plugin_dl->dl.length));
      DBUG_RETURN(TRUE);
    }
    /*
      All st_plugin fields not initialized in the plugin explicitly, are
      set to 0. It matches C standard behaviour for struct initializers that
      have less values than the struct definition.
    */
    for (i=0;
         (old= (struct st_mysql_plugin *)(ptr + i * sizeof_st_plugin))->info;
         i++)
    {

      cur[i].type= old->type;
      cur[i].info= old->info;
      cur[i].name= old->name;
      cur[i].author= old->author;
      cur[i].descr= old->descr;
      cur[i].license= old->license;
      cur[i].init= old->init;
      cur[i].deinit= old->deinit;
      cur[i].version= old->version;
      cur[i].status_vars= old->status_vars;
      cur[i].system_vars= old->system_vars;
      /*
        Something like this should be added to process
        new mysql plugin versions:
        if (plugin_dl->mysqlversion > 0x0101)
        {
           cur[i].newfield= CONSTANT_MEANS_UNKNOWN;
        }
        else
        {
           cur[i].newfield= old->newfield;
        }
      */
      /* Maria only fields */
      cur[i].version_info= "Unknown";
      cur[i].maturity= MariaDB_PLUGIN_MATURITY_UNKNOWN;
    }
    plugin_dl->allocated= true;
    plugin_dl->plugins= (struct st_maria_plugin *)cur;
  }

  DBUG_RETURN(FALSE);
}


void plugin_thdvar_init(THD *thd)
{
  plugin_ref old_table_plugin= thd->variables.table_plugin;
  plugin_ref old_tmp_table_plugin= thd->variables.tmp_table_plugin;
  plugin_ref old_enforced_table_plugin= thd->variables.enforced_table_plugin;
  DBUG_ENTER("plugin_thdvar_init");

  // This function may be called many times per THD (e.g. on COM_CHANGE_USER)
  thd->variables.table_plugin= NULL;
  thd->variables.tmp_table_plugin= NULL;
  thd->variables.enforced_table_plugin= NULL;
  cleanup_variables(&thd->variables);

  thd->variables= global_system_variables;

  /* we are going to allocate these lazily */
  thd->variables.dynamic_variables_version= 0;
  thd->variables.dynamic_variables_size= 0;
  thd->variables.dynamic_variables_ptr= 0;

  mysql_mutex_lock(&LOCK_plugin);
  thd->variables.table_plugin=
      intern_plugin_lock(NULL, global_system_variables.table_plugin);
  if (global_system_variables.tmp_table_plugin)
    thd->variables.tmp_table_plugin=
          intern_plugin_lock(NULL, global_system_variables.tmp_table_plugin);
  if (global_system_variables.enforced_table_plugin)
    thd->variables.enforced_table_plugin=
          intern_plugin_lock(NULL, global_system_variables.enforced_table_plugin);
  intern_plugin_unlock(NULL, old_table_plugin);
  intern_plugin_unlock(NULL, old_tmp_table_plugin);
  intern_plugin_unlock(NULL, old_enforced_table_plugin);
  mysql_mutex_unlock(&LOCK_plugin);

  DBUG_VOID_RETURN;
}


virtual void global_save_default(THD *thd, set_var *var) {}


static st_bookmark *register_var(const char *plugin, const char *name,
                                 int flags)
{
  uint length= strlen(plugin) + strlen(name) + 3, size, offset, new_size;
  st_bookmark *result;
  char *varname, *p;

  DBUG_ASSERT(flags & PLUGIN_VAR_THDLOCAL);

  size= var_storage_size(flags);
  varname= ((char*) my_alloca(length));
  strxmov(varname + 1, plugin, "_", name, NullS);
  for (p= varname + 1; *p; p++)
    if (*p == '-')
      *p= '_';

  if (!(result= find_bookmark(NULL, varname + 1, flags)))
  {
    result= (st_bookmark*) alloc_root(&plugin_vars_mem_root,
                                      sizeof(struct st_bookmark) + length-1);
    varname[0]= plugin_var_bookmark_key(flags);
    memcpy(result->key, varname, length);
    result->name_len= length - 2;
    result->offset= -1;

    DBUG_ASSERT(size && !(size & (size-1))); /* must be power of 2 */

    offset= global_system_variables.dynamic_variables_size;
    offset= (offset + size - 1) & ~(size - 1);
    result->offset= (int) offset;

    new_size= (offset + size + 63) & ~63;

    if (new_size > global_variables_dynamic_size)
    {
      global_system_variables.dynamic_variables_ptr= (char*)
        my_realloc(global_system_variables.dynamic_variables_ptr, new_size,
                   MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
      max_system_variables.dynamic_variables_ptr= (char*)
        my_realloc(max_system_variables.dynamic_variables_ptr, new_size,
                   MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
      /*
        Clear the new variable value space. This is required for string
        variables. If their value is non-NULL, it must point to a valid
        string.
      */
      bzero(global_system_variables.dynamic_variables_ptr +
            global_variables_dynamic_size,
            new_size - global_variables_dynamic_size);
      bzero(max_system_variables.dynamic_variables_ptr +
            global_variables_dynamic_size,
            new_size - global_variables_dynamic_size);
      global_variables_dynamic_size= new_size;
    }

    global_system_variables.dynamic_variables_head= offset;
    max_system_variables.dynamic_variables_head= offset;
    global_system_variables.dynamic_variables_size= offset + size;
    max_system_variables.dynamic_variables_size= offset + size;
    global_system_variables.dynamic_variables_version++;
    max_system_variables.dynamic_variables_version++;

    result->version= global_system_variables.dynamic_variables_version;

    /* this should succeed because we have already checked if a dup exists */
    if (my_hash_insert(&bookmark_hash, (uchar*) result))
    {
      fprintf(stderr, "failed to add placeholder to hash");
      DBUG_ASSERT(0);
    }
  }
  my_afree(varname);
  return result;
}


int thd_key_create(MYSQL_THD_KEY_T *key)
{
  int flags= PLUGIN_VAR_THDLOCAL | PLUGIN_VAR_STR |
             PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT;
  char namebuf[256];
  snprintf(namebuf, sizeof(namebuf), "%u", thd_key_no++);
  mysql_prlock_wrlock(&LOCK_system_variables_hash);
  // non-letters in the name as an extra safety
  st_bookmark *bookmark= register_var("\a\v\a\t\a\r", namebuf, flags);
  mysql_prlock_unlock(&LOCK_system_variables_hash);
  if (bookmark)
  {
    *key= bookmark->offset;
    return 0;
  }
  return ENOMEM;
}


void plugin_unlock_list(THD *thd, plugin_ref *list, uint count)
{
  LEX *lex= thd ? thd->lex : 0;
  DBUG_ENTER("plugin_unlock_list");
  if (count == 0)
    DBUG_VOID_RETURN;

  DBUG_ASSERT(list);
  mysql_mutex_lock(&LOCK_plugin);
  while (count--)
    intern_plugin_unlock(lex, *list++);
  reap_plugins();
  mysql_mutex_unlock(&LOCK_plugin);
  DBUG_VOID_RETURN;
}


bool plugin_is_forced(struct st_plugin_int *p)
{
  return p->load_option == PLUGIN_FORCE ||
         p->load_option == PLUGIN_FORCE_PLUS_PERMANENT;
}


uchar *get_plugin_hash_key(const uchar *buff, size_t *length,
                           my_bool not_used __attribute__((unused)))
{
  struct st_plugin_int *plugin= (st_plugin_int *)buff;
  *length= (uint)plugin->name.length;
  return((uchar *)plugin->name.str);
}


sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
                         bool throw_error, bool locked)
{
  sys_var *var;
  sys_var_pluginvar *pi= NULL;
  plugin_ref plugin;
  DBUG_ENTER("find_sys_var_ex");
  DBUG_PRINT("enter", ("var '%.*s'", (int)length, str));

  if (!locked)
    mysql_mutex_lock(&LOCK_plugin);
  mysql_prlock_rdlock(&LOCK_system_variables_hash);
  if ((var= intern_find_sys_var(str, length)) &&
      (pi= var->cast_pluginvar()))
  {
    mysql_prlock_unlock(&LOCK_system_variables_hash);
    LEX *lex= thd ? thd->lex : 0;
    if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
      var= NULL; /* failed to lock it, it must be uninstalling */
    else
    if (!(plugin_state(plugin) & PLUGIN_IS_READY))
    {
      /* initialization not completed */
      var= NULL;
      intern_plugin_unlock(lex, plugin);
    }
  }
  else
    mysql_prlock_unlock(&LOCK_system_variables_hash);
  if (!locked)
    mysql_mutex_unlock(&LOCK_plugin);

  if (!throw_error && !var)
    my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str);
  DBUG_RETURN(var);
}


uchar *get_bookmark_hash_key(const uchar *buff, size_t *length,
                             my_bool not_used __attribute__((unused)))
{
  struct st_bookmark *var= (st_bookmark *)buff;
  *length= var->name_len + 1;
  return (uchar*) var->key;
}


plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type)
{
  LEX *lex= thd ? thd->lex : 0;
  plugin_ref rc= NULL;
  st_plugin_int *plugin;
  DBUG_ENTER("plugin_lock_by_name");
  mysql_mutex_lock(&LOCK_plugin);
  if ((plugin= plugin_find_internal(name, type)))
    rc= intern_plugin_lock(lex, plugin_int_to_ref(plugin));
  mysql_mutex_unlock(&LOCK_plugin);
  DBUG_RETURN(rc);
}


void wsrep_plugins_post_init()
{
  THD *thd;
  I_List_iterator<THD> it(threads);

  while ((thd= it++))
  {
    if (IF_WSREP(thd->wsrep_applier,1))
    {
      // Save options_bits as it will get overwritten in plugin_thdvar_init()
      ulonglong option_bits_saved= thd->variables.option_bits;

      plugin_thdvar_init(thd);

      // Restore option_bits
      thd->variables.option_bits= option_bits_saved;
    }
  }

  return;
}


static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name,
                             int *argc, char **argv)
{
  struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN);
  int error;
  DBUG_ASSERT(tmp);
  mysql_mutex_assert_owner(&LOCK_plugin); // because of tmp->state

  if (tmp->state != PLUGIN_IS_UNINITIALIZED)
  {
    /* already installed */
    return 0;
  }
  else
  {
    if (plugin_initialize(thd->mem_root, tmp, argc, argv, false))
    {
      report_error(REPORT_TO_USER, ER_CANT_INITIALIZE_UDF, name->str,
                   "Plugin initialization function failed.");
      tmp->state= PLUGIN_IS_DELETED;
      return 1;
    }
  }
  if (tmp->state == PLUGIN_IS_DISABLED)
  {
    if (global_system_variables.log_warnings)
      push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                          ER_CANT_INITIALIZE_UDF,
                          ER_THD(thd, ER_CANT_INITIALIZE_UDF),
                          name->str, "Plugin is disabled");
  }

  /*
    We do not replicate the INSTALL PLUGIN statement. Disable binlogging
    of the insert into the plugin table, so that it is not replicated in
    row based mode.
  */
  tmp_disable_binlog(thd);
  table->use_all_columns();
  restore_record(table, s->default_values);
  table->field[0]->store(name->str, name->length, system_charset_info);
  table->field[1]->store(tmp->plugin_dl->dl.str, tmp->plugin_dl->dl.length,
                         files_charset_info);
  error= table->file->ha_write_row(table->record[0]);
  reenable_binlog(thd);
  if (error)
  {
    table->file->print_error(error, MYF(0));
    tmp->state= PLUGIN_IS_DELETED;
    return 1;
  }
  return 0;
}


static bool plugin_add(MEM_ROOT *tmp_root,
                       const LEX_STRING *name, LEX_STRING *dl, int report)
{
  struct st_plugin_int tmp, *maybe_dupe;
  struct st_maria_plugin *plugin;
  uint oks= 0, errs= 0, dupes= 0;
  DBUG_ENTER("plugin_add");
  DBUG_PRINT("enter", ("name: %s  dl: %s", name->str, dl->str));

  if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN))
  {
    report_error(report, ER_PLUGIN_INSTALLED, name->str);
    DBUG_RETURN(TRUE);
  }
  /* Clear the whole struct to catch future extensions. */
  bzero((char*) &tmp, sizeof(tmp));
  fix_dl_name(tmp_root, dl);
  if (! (tmp.plugin_dl= plugin_dl_add(dl, report)))
    DBUG_RETURN(TRUE);
  /* Find plugin by name */
  for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++)
  {
    tmp.name.str= (char *)plugin->name;
    tmp.name.length= strlen(plugin->name);

    if (plugin->type < 0 || plugin->type >= MYSQL_MAX_PLUGIN_TYPE_NUM)
      continue; // invalid plugin type

    if (plugin->type == MYSQL_UDF_PLUGIN ||
        (plugin->type == MariaDB_PASSWORD_VALIDATION_PLUGIN &&
         tmp.plugin_dl->mariaversion == 0))
      continue; // unsupported plugin type

    if (name->str && my_strnncoll(system_charset_info,
                                  (const uchar *)name->str, name->length,
                                  (const uchar *)tmp.name.str, tmp.name.length))
      continue; // plugin name doesn't match

    if (!name->str &&
        (maybe_dupe= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)))
    {
      if (plugin->name != maybe_dupe->plugin->name)
      {
        report_error(report, ER_UDF_EXISTS, plugin->name);
        DBUG_RETURN(TRUE);
      }
      dupes++;
      continue; // already installed
    }
    struct st_plugin_int *tmp_plugin_ptr;
    if (*(int*)plugin->info <
        min_plugin_info_interface_version[plugin->type] ||
        ((*(int*)plugin->info) >> 8) >
        (cur_plugin_info_interface_version[plugin->type] >> 8))
    {
      char buf[256];
      strxnmov(buf, sizeof(buf) - 1, "API version for ",
               plugin_type_names[plugin->type].str,
               " plugin ", tmp.name.str,
               " not supported by this version of the server", NullS);
      report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, ENOEXEC, buf);
      goto err;
    }
    if (plugin_maturity_map[plugin->maturity] < plugin_maturity)
    {
      char buf[256];
      strxnmov(buf, sizeof(buf) - 1, "Loading of ",
               plugin_maturity_names[plugin->maturity],
               " plugin ", tmp.name.str,
               " is prohibited by --plugin-maturity=",
               plugin_maturity_names[plugin_maturity],
               NullS);
      report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, EPERM, buf);
      goto err;
    }
    tmp.plugin= plugin;
    tmp.ref_count= 0;
    tmp.state= PLUGIN_IS_UNINITIALIZED;
    tmp.load_option= PLUGIN_ON;

    if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
      goto err;
    if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
      tmp_plugin_ptr->state= PLUGIN_IS_FREED;
    init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096, MYF(0));

    if (name->str)
      DBUG_RETURN(FALSE); // all done

    oks++;
    tmp.plugin_dl->ref_count++;
    continue; // otherwise - go on

err:
    errs++;
    if (name->str)
      break;
  }

  DBUG_ASSERT(!name->str || !dupes); // dupes is ONLY for name->str == 0

  if (errs == 0 && oks == 0 && !dupes) // no plugin was found
    report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);

  plugin_dl_del(tmp.plugin_dl);
  DBUG_RETURN(errs > 0 || oks + dupes == 0);
}


static int check_func_bool(THD *thd, struct st_mysql_sys_var *var,
                           void *save, st_mysql_value *value)
{
  char buff[STRING_BUFFER_USUAL_SIZE];
  const char *str;
  int result, length;
  long long tmp;

  if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
  {
    length= sizeof(buff);
    if (!(str= value->val_str(value, buff, &length)) ||
        (result= find_type(&bool_typelib, str, length, 1)-1) < 0)
      goto err;
  }
  else
  {
    if (value->val_int(value, &tmp) < 0)
      goto err;
    if (tmp != 0 && tmp != 1)
      goto err;
    result= (int) tmp;
  }
  *(my_bool *) save= result ? 1 : 0;
  return 0;
err:
  return 1;
}


static void fix_dl_name(MEM_ROOT *root, LEX_STRING *dl)
{
  const size_t so_ext_len= sizeof(SO_EXT) - 1;
  if (dl->length < so_ext_len ||
      my_strcasecmp(&my_charset_latin1, dl->str + dl->length - so_ext_len,
                    SO_EXT))
  {
    char *s= (char*)alloc_root(root, dl->length + so_ext_len + 1);
    memcpy(s, dl->str, dl->length);
    strcpy(s + dl->length, SO_EXT);
    dl->str= s;
    dl->length+= so_ext_len;
  }
}


static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
{
  DBUG_ENTER("intern_sys_var_ptr");
  DBUG_ASSERT(offset >= 0);
  DBUG_ASSERT((uint)offset <= global_system_variables.dynamic_variables_head);

  if (!thd)
    DBUG_RETURN((uchar*) global_system_variables.dynamic_variables_ptr + offset);

  /*
    dynamic_variables_head points to the largest valid offset
  */
  if (!thd->variables.dynamic_variables_ptr ||
      (uint)offset > thd->variables.dynamic_variables_head)
  {
    mysql_prlock_rdlock(&LOCK_system_variables_hash);
    sync_dynamic_session_variables(thd, global_lock);
    mysql_prlock_unlock(&LOCK_system_variables_hash);
  }
  DBUG_RETURN((uchar*)thd->variables.dynamic_variables_ptr + offset);
}


void sync_dynamic_session_variables(THD* thd, bool global_lock)
{
  uint idx;

  thd->variables.dynamic_variables_ptr= (char*)
    my_realloc(thd->variables.dynamic_variables_ptr,
               global_variables_dynamic_size,
               MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));

  if (global_lock)
    mysql_mutex_lock(&LOCK_global_system_variables);

  mysql_mutex_assert_owner(&LOCK_global_system_variables);

  memcpy(thd->variables.dynamic_variables_ptr +
           thd->variables.dynamic_variables_size,
         global_system_variables.dynamic_variables_ptr +
           thd->variables.dynamic_variables_size,
         global_system_variables.dynamic_variables_size -
           thd->variables.dynamic_variables_size);

  /*
    now we need to iterate through any newly copied 'defaults'
    and if it is a string type with MEMALLOC flag, we need to strdup
  */
  for (idx= 0; idx < bookmark_hash.records; idx++)
  {
    st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx);

    if (v->version <= thd->variables.dynamic_variables_version)
      continue; /* already in thd->variables */

    /* Here we do anything special that may be required of the data types */

    if ((v->key[0] & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
         v->key[0] & BOOKMARK_MEMALLOC)
    {
      char **pp= (char**) (thd->variables.dynamic_variables_ptr + v->offset);
      if (*pp)
        *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE));
    }
  }

  if (global_lock)
    mysql_mutex_unlock(&LOCK_global_system_variables);

  thd->variables.dynamic_variables_version=
         global_system_variables.dynamic_variables_version;
  thd->variables.dynamic_variables_head=
         global_system_variables.dynamic_variables_head;
  thd->variables.dynamic_variables_size=
         global_system_variables.dynamic_variables_size;
}


static int item_val_int(struct st_mysql_value *value, long long *buf)
{
  Item *item= ((st_item_value_holder*)value)->item;
  *buf= item->val_int();
  if (item->is_null())
    return 1;
  return 0;
}


static int item_is_unsigned(struct st_mysql_value *value)
{
  Item *item= ((st_item_value_holder*)value)->item;
  return item->unsigned_flag;
}


static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *dl)
{
  uint i;
  struct st_plugin_dl *tmp;
  DBUG_ENTER("plugin_dl_find");
  for (i= 0; i < plugin_dl_array.elements; i++)
  {
    tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **);
    if (tmp->ref_count &&
        ! my_strnncoll(files_charset_info,
                       (const uchar *)dl->str, dl->length,
                       (const uchar *)tmp->dl.str, tmp->dl.length))
      DBUG_RETURN(tmp);
  }
  DBUG_RETURN(0);
}


static void plugin_variables_deinit(struct st_plugin_int *plugin)
{

  for (sys_var *var= plugin->system_vars; var; var= var->next)
    (*var->test_load)= FALSE;
  mysql_del_sys_var_chain(plugin->system_vars);
}


static int check_func_enum(THD *thd, struct st_mysql_sys_var *var,
                           void *save, st_mysql_value *value)
{
  char buff[STRING_BUFFER_USUAL_SIZE];
  const char *str;
  TYPELIB *typelib;
  long long tmp;
  long result;
  int length;

  if (var->flags & PLUGIN_VAR_THDLOCAL)
    typelib= ((thdvar_enum_t*) var)->typelib;
  else
    typelib= ((sysvar_enum_t*) var)->typelib;

  if (value->value_type(value) == MYSQL_VALUE_TYPE_STRING)
  {
    length= sizeof(buff);
    if (!(str= value->val_str(value, buff, &length)))
      goto err;
    if ((result= (long)find_type(typelib, str, length, 0) - 1) < 0)
      goto err;
  }
  else
  {
    if (value->val_int(value, &tmp))
      goto err;
    if (tmp < 0 || tmp >= typelib->count)
      goto err;
    result= (long) tmp;
  }
  *(long*)save= result;
  return 0;
err:
  return 1;
}


static int check_func_double(THD *thd, struct st_mysql_sys_var *var,
                             void *save, st_mysql_value *value)
{
  double v;
  my_bool fixed;
  struct my_option option;

  value->val_real(value, &v);
  plugin_opt_set_limits(&option, var);
  *(double *) save= getopt_double_limit_value(v, &option, &fixed);

  return throw_bounds_warning(thd, var->name, fixed, v);
}