diff options
Diffstat (limited to 'sql/sql_plugin.cc')
-rw-r--r-- | sql/sql_plugin.cc | 3314 |
1 files changed, 3314 insertions, 0 deletions
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc new file mode 100644 index 00000000000..60f205ec8e8 --- /dev/null +++ b/sql/sql_plugin.cc @@ -0,0 +1,3314 @@ +/* Copyright (C) 2005 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" +#include <my_pthread.h> +#include <my_getopt.h> +#define REPORT_TO_LOG 1 +#define REPORT_TO_USER 2 + +#ifdef DBUG_OFF +#define plugin_ref_to_int(A) A +#define plugin_int_to_ref(A) A +#else +#define plugin_ref_to_int(A) (A ? A[0] : NULL) +#define plugin_int_to_ref(A) &(A) +#endif + +extern struct st_mysql_plugin *mysqld_builtins[]; + +char *opt_plugin_load= NULL; +char *opt_plugin_dir_ptr; +char opt_plugin_dir[FN_REFLEN]; +/* + When you ad a new plugin type, add both a string and make sure that the + init and deinit array are correctly updated. +*/ +const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]= +{ + { C_STRING_WITH_LEN("UDF") }, + { C_STRING_WITH_LEN("STORAGE ENGINE") }, + { C_STRING_WITH_LEN("FTPARSER") }, + { C_STRING_WITH_LEN("DAEMON") }, + { C_STRING_WITH_LEN("INFORMATION SCHEMA") } +}; + +extern int initialize_schema_table(st_plugin_int *plugin); +extern int finalize_schema_table(st_plugin_int *plugin); + +/* + The number of elements in both plugin_type_initialize and + plugin_type_deinitialize should equal to the number of plugins + defined. +*/ +plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= +{ + 0,ha_initialize_handlerton,0,0,initialize_schema_table +}; + +plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= +{ + 0,ha_finalize_handlerton,0,0,finalize_schema_table +}; + +#ifdef HAVE_DLOPEN +static const char *plugin_interface_version_sym= + "_mysql_plugin_interface_version_"; +static const char *sizeof_st_plugin_sym= + "_mysql_sizeof_struct_st_plugin_"; +static const char *plugin_declarations_sym= "_mysql_plugin_declarations_"; +static int min_plugin_interface_version= MYSQL_PLUGIN_INTERFACE_VERSION & ~0xFF; +#endif + +/* Note that 'int version' must be the first field of every plugin + sub-structure (plugin->info). +*/ +static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= +{ + 0x0000, + MYSQL_HANDLERTON_INTERFACE_VERSION, + MYSQL_FTPARSER_INTERFACE_VERSION, + MYSQL_DAEMON_INTERFACE_VERSION, + MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION +}; +static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= +{ + 0x0000, /* UDF: not implemented */ + MYSQL_HANDLERTON_INTERFACE_VERSION, + MYSQL_FTPARSER_INTERFACE_VERSION, + MYSQL_DAEMON_INTERFACE_VERSION, + MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION +}; + +static bool initialized= 0; + +/* + A mutex LOCK_plugin must be acquired before accessing the + following variables/structures. + We are always manipulating ref count, so a rwlock here is unneccessary. +*/ +pthread_mutex_t LOCK_plugin; +static DYNAMIC_ARRAY plugin_dl_array; +static DYNAMIC_ARRAY plugin_array; +static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; +static bool reap_needed= false; +static int plugin_array_version=0; + +/* + write-lock on LOCK_system_variables_hash is required before modifying + the following variables/structures +*/ +static MEM_ROOT plugin_mem_root; +static uint global_variables_dynamic_size= 0; +static HASH bookmark_hash; + + +/* + hidden part of opaque value passed to variable check functions. + Used to provide a object-like structure to non C++ consumers. +*/ +struct st_item_value_holder : public st_mysql_value +{ + Item *item; +}; + + +/* + stored in bookmark_hash, this structure is never removed from the + hash and is used to mark a single offset for a thd local variable + even if plugins have been uninstalled and reinstalled, repeatedly. + This structure is allocated from plugin_mem_root. + + The key format is as follows: + 1 byte - variable type code + name_len bytes - variable name + '\0' - end of key +*/ +struct st_bookmark +{ + uint name_len; + int offset; + uint version; + char key[1]; +}; + + +/* + skeleton of a plugin variable - portion of structure common to all. +*/ +struct st_mysql_sys_var +{ + MYSQL_PLUGIN_VAR_HEADER; +}; + + +/* + sys_var class for access to all plugin variables visible to the user +*/ +class sys_var_pluginvar: public sys_var +{ +public: + struct st_plugin_int *plugin; + struct st_mysql_sys_var *plugin_var; + + static void *operator new(size_t size, MEM_ROOT *mem_root) + { return (void*) alloc_root(mem_root, (uint) size); } + static void operator delete(void *ptr_arg,size_t size) + { TRASH(ptr_arg, size); } + + sys_var_pluginvar(const char *name_arg, + struct st_mysql_sys_var *plugin_var_arg) + :sys_var(name_arg), plugin_var(plugin_var_arg) {} + sys_var_pluginvar *cast_pluginvar() { return this; } + bool is_readonly() const { return plugin_var->flags & PLUGIN_VAR_READONLY; } + bool check_type(enum_var_type type) + { return !(plugin_var->flags & PLUGIN_VAR_THDLOCAL) && type != OPT_GLOBAL; } + bool check_update_type(Item_result type); + SHOW_TYPE show_type(); + uchar* real_value_ptr(THD *thd, enum_var_type type); + TYPELIB* plugin_var_typelib(void); + uchar* value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); + bool check(THD *thd, set_var *var); + bool check_default(enum_var_type type) { return is_readonly(); } + void set_default(THD *thd, enum_var_type type); + bool update(THD *thd, set_var *var); +}; + + +/* prototypes */ +static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv); +static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, + const char *list); +static int test_plugin_options(MEM_ROOT *, struct st_plugin_int *, + int *, char **, my_bool); +static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *, + struct st_plugin_int **); +static void unlock_variables(THD *thd, struct system_variables *vars); +static void cleanup_variables(THD *thd, struct system_variables *vars); +static void plugin_vars_free_values(sys_var *vars); +static void plugin_opt_set_limits(struct my_option *options, + const struct st_mysql_sys_var *opt); +#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B CALLER_INFO) +#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B ORIG_CALLER_INFO) +static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin + CALLER_INFO_PROTO); +static void intern_plugin_unlock(LEX *lex, plugin_ref plugin); +static void reap_plugins(void); + + +/* declared in set_var.cc */ +extern sys_var *intern_find_sys_var(const char *str, uint length, bool no_error); +extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, + const char *name, longlong val); + +#ifdef EMBEDDED_LIBRARY +/* declared in sql_base.cc */ +extern bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists); +#endif /* EMBEDDED_LIBRARY */ + + +/**************************************************************************** + Value type thunks, allows the C world to play in the C++ world +****************************************************************************/ + +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 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->c_ptr_quick(), res->length()); +} + + +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_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; +} + + +/**************************************************************************** + Plugin support code +****************************************************************************/ + +#ifdef HAVE_DLOPEN + +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 st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl) +{ + uint i; + struct st_plugin_dl *tmp; + DBUG_ENTER("plugin_dl_insert_or_reuse"); + for (i= 0; i < plugin_dl_array.elements; i++) + { + tmp= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **); + if (! tmp->ref_count) + { + memcpy(tmp, plugin_dl, sizeof(struct st_plugin_dl)); + DBUG_RETURN(tmp); + } + } + if (insert_dynamic(&plugin_dl_array, (uchar*)&plugin_dl)) + DBUG_RETURN(0); + tmp= *dynamic_element(&plugin_dl_array, plugin_dl_array.elements - 1, + struct st_plugin_dl **)= + (struct st_plugin_dl *) memdup_root(&plugin_mem_root, (uchar*)plugin_dl, + sizeof(struct st_plugin_dl)); + DBUG_RETURN(tmp); +} +#endif /* HAVE_DLOPEN */ + + +static inline void free_plugin_mem(struct st_plugin_dl *p) +{ +#ifdef HAVE_DLOPEN + if (p->handle) + dlclose(p->handle); +#endif + my_free(p->dl.str, MYF(MY_ALLOW_ZERO_PTR)); + if (p->version != MYSQL_PLUGIN_INTERFACE_VERSION) + my_free((uchar*)p->plugins, MYF(MY_ALLOW_ZERO_PTR)); +} + + +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, dlpathlen; + struct st_plugin_dl *tmp, plugin_dl; + void *sym; + DBUG_ENTER("plugin_dl_add"); + 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 (my_strchr(files_charset_info, dl->str, dl->str + dl->length, FN_LIBCHAR) || + check_string_char_length((LEX_STRING *) dl, "", NAME_CHAR_LEN, + system_charset_info, 1) || + plugin_dir_len + dl->length + 1 >= FN_REFLEN) + { + if (report & REPORT_TO_USER) + my_error(ER_UDF_NO_PATHS, MYF(0)); + if (report & REPORT_TO_LOG) + sql_print_error(ER(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 */ + dlpathlen= + strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", dl->str, NullS) - + dlpath; + plugin_dl.ref_count= 1; + /* Open new dll handle */ + if (!(plugin_dl.handle= dlopen(dlpath, RTLD_NOW))) + { + const char *errmsg=dlerror(); + if (!strncmp(dlpath, errmsg, dlpathlen)) + { // if errmsg starts from dlpath, trim this prefix. + errmsg+=dlpathlen; + if (*errmsg == ':') errmsg++; + if (*errmsg == ' ') errmsg++; + } + if (report & REPORT_TO_USER) + my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, errno, errmsg); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, errno, errmsg); + DBUG_RETURN(0); + } + /* Determine interface version */ + if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym))) + { + free_plugin_mem(&plugin_dl); + if (report & REPORT_TO_USER) + my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_interface_version_sym); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_interface_version_sym); + DBUG_RETURN(0); + } + plugin_dl.version= *(int *)sym; + /* Versioning */ + if (plugin_dl.version < min_plugin_interface_version || + (plugin_dl.version >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8)) + { + free_plugin_mem(&plugin_dl); + if (report & REPORT_TO_USER) + my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, 0, + "plugin interface version mismatch"); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, 0, + "plugin interface version mismatch"); + DBUG_RETURN(0); + } + /* Find plugin declarations */ + if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym))) + { + free_plugin_mem(&plugin_dl); + if (report & REPORT_TO_USER) + my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_declarations_sym); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_declarations_sym); + DBUG_RETURN(0); + } + + if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION) + { + int i; + uint sizeof_st_plugin; + struct st_mysql_plugin *old, *cur; + char *ptr= (char *)sym; + + if ((sym= dlsym(plugin_dl.handle, sizeof_st_plugin_sym))) + sizeof_st_plugin= *(int *)sym; + else + { +#ifdef ERROR_ON_NO_SIZEOF_PLUGIN_SYMBOL + free_plugin_mem(&plugin_dl); + if (report & REPORT_TO_USER) + my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), sizeof_st_plugin_sym); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), sizeof_st_plugin_sym); + DBUG_RETURN(0); +#else + /* + When the following assert starts failing, we'll have to switch + to the upper branch of the #ifdef + */ + DBUG_ASSERT(min_plugin_interface_version == 0); + sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version); +#endif + } + + for (i= 0; + ((struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info; + i++) + /* no op */; + + cur= (struct st_mysql_plugin*) + my_malloc(i*sizeof(struct st_mysql_plugin), MYF(MY_ZEROFILL|MY_WME)); + if (!cur) + { + free_plugin_mem(&plugin_dl); + if (report & REPORT_TO_USER) + my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length); + DBUG_RETURN(0); + } + /* + 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++) + memcpy(cur+i, old, min(sizeof(cur[i]), sizeof_st_plugin)); + + sym= cur; + } + plugin_dl.plugins= (struct st_mysql_plugin *)sym; + + /* 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)))) + { + free_plugin_mem(&plugin_dl); + if (report & REPORT_TO_USER) + my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length); + DBUG_RETURN(0); + } + 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))) + { + free_plugin_mem(&plugin_dl); + if (report & REPORT_TO_USER) + my_error(ER_OUTOFMEMORY, MYF(0), sizeof(struct st_plugin_dl)); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_OUTOFMEMORY), sizeof(struct st_plugin_dl)); + DBUG_RETURN(0); + } + DBUG_RETURN(tmp); +#else + DBUG_ENTER("plugin_dl_add"); + if (report & REPORT_TO_USER) + my_error(ER_FEATURE_DISABLED, MYF(0), "plugin", "HAVE_DLOPEN"); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_FEATURE_DISABLED), "plugin", "HAVE_DLOPEN"); + DBUG_RETURN(0); +#endif +} + + +static void plugin_dl_del(const LEX_STRING *dl) +{ +#ifdef HAVE_DLOPEN + uint i; + DBUG_ENTER("plugin_dl_del"); + + safe_mutex_assert_owner(&LOCK_plugin); + + for (i= 0; i < plugin_dl_array.elements; i++) + { + struct st_plugin_dl *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)) + { + /* Do not remove this element, unless no other plugin uses this dll. */ + if (! --tmp->ref_count) + { + free_plugin_mem(tmp); + bzero(tmp, sizeof(struct st_plugin_dl)); + } + break; + } + } + DBUG_VOID_RETURN; +#endif +} + + +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); + + safe_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 *) + hash_search(&plugin_hash[i], (const uchar *)name->str, name->length); + if (plugin) + DBUG_RETURN(plugin); + } + } + else + DBUG_RETURN((st_plugin_int *) + hash_search(&plugin_hash[type], (const uchar *)name->str, name->length)); + DBUG_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"); + pthread_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; + } + pthread_mutex_unlock(&LOCK_plugin); + DBUG_RETURN(rc); +} + + +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; +} + + +SHOW_COMP_OPTION sys_var_have_plugin::get_option() +{ + LEX_STRING plugin_name= { (char *) plugin_name_str, plugin_name_len }; + return plugin_status(&plugin_name, plugin_type); +} + + +static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO) +{ + st_plugin_int *pi= plugin_ref_to_int(rc); + DBUG_ENTER("intern_plugin_lock"); + + safe_mutex_assert_owner(&LOCK_plugin); + + if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED)) + { + plugin_ref plugin; +#ifdef DBUG_OFF + /* built-in plugins don't need ref counting */ + 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_ci(sizeof(pi), MYF(MY_WME)))) + DBUG_RETURN(NULL); + + *plugin= pi; +#endif + pi->ref_count++; + DBUG_PRINT("info",("thd: 0x%lx, plugin: \"%s\", ref_count: %d", + (long) current_thd, pi->name.str, pi->ref_count)); + + if (lex) + insert_dynamic(&lex->plugins, (uchar*)&plugin); + DBUG_RETURN(plugin); + } + DBUG_RETURN(NULL); +} + + +plugin_ref plugin_lock(THD *thd, plugin_ref *ptr CALLER_INFO_PROTO) +{ + LEX *lex= thd ? thd->lex : 0; + plugin_ref rc; + DBUG_ENTER("plugin_lock"); + pthread_mutex_lock(&LOCK_plugin); + rc= my_intern_plugin_lock_ci(lex, *ptr); + pthread_mutex_unlock(&LOCK_plugin); + DBUG_RETURN(rc); +} + + +plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type + CALLER_INFO_PROTO) +{ + LEX *lex= thd ? thd->lex : 0; + plugin_ref rc= NULL; + st_plugin_int *plugin; + DBUG_ENTER("plugin_lock_by_name"); + pthread_mutex_lock(&LOCK_plugin); + if ((plugin= plugin_find_internal(name, type))) + rc= my_intern_plugin_lock_ci(lex, plugin_int_to_ref(plugin)); + pthread_mutex_unlock(&LOCK_plugin); + DBUG_RETURN(rc); +} + + +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); +} + + +/* + NOTE + Requires that a write-lock is held on LOCK_system_variables_hash +*/ +static bool plugin_add(MEM_ROOT *tmp_root, + const LEX_STRING *name, const LEX_STRING *dl, + int *argc, char **argv, int report) +{ + struct st_plugin_int tmp; + struct st_mysql_plugin *plugin; + DBUG_ENTER("plugin_add"); + if (plugin_find_internal(name, MYSQL_ANY_PLUGIN)) + { + if (report & REPORT_TO_USER) + my_error(ER_UDF_EXISTS, MYF(0), name->str); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_UDF_EXISTS), name->str); + DBUG_RETURN(TRUE); + } + /* Clear the whole struct to catch future extensions. */ + bzero((char*) &tmp, sizeof(tmp)); + 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++) + { + uint name_len= strlen(plugin->name); + if (plugin->type >= 0 && plugin->type < MYSQL_MAX_PLUGIN_TYPE_NUM && + ! my_strnncoll(system_charset_info, + (const uchar *)name->str, name->length, + (const uchar *)plugin->name, + name_len)) + { + 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 is too different", NullS); + if (report & REPORT_TO_USER) + my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dl->str, 0, buf); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dl->str, 0, buf); + goto err; + } + tmp.plugin= plugin; + tmp.name.str= (char *)plugin->name; + tmp.name.length= name_len; + tmp.ref_count= 0; + tmp.state= PLUGIN_IS_UNINITIALIZED; + if (test_plugin_options(tmp_root, &tmp, argc, argv, true)) + tmp.state= PLUGIN_IS_DISABLED; + + if ((tmp_plugin_ptr= plugin_insert_or_reuse(&tmp))) + { + plugin_array_version++; + if (!my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr)) + { + init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096); + DBUG_RETURN(FALSE); + } + tmp_plugin_ptr->state= PLUGIN_IS_FREED; + } + mysql_del_sys_var_chain(tmp.system_vars); + goto err; + + /* plugin was disabled */ + plugin_dl_del(dl); + DBUG_RETURN(FALSE); + } + } + if (report & REPORT_TO_USER) + my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), name->str); + if (report & REPORT_TO_LOG) + sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), name->str); +err: + plugin_dl_del(dl); + DBUG_RETURN(TRUE); +} + + +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 + */ + safe_mutex_assert_not_owner(&LOCK_plugin); + + if (plugin->plugin->status_vars) + { +#ifdef FIX_LATER + /* + We have a problem right now where we can not prepend without + breaking backwards compatibility. We will fix this shortly so + that engines have "use names" and we wil use those for + CREATE TABLE, and use the plugin name then for adding automatic + variable names. + */ + SHOW_VAR array[2]= { + {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY}, + {0, 0, SHOW_UNDEF} + }; + remove_status_vars(array); +#else + remove_status_vars(plugin->plugin->status_vars); +#endif /* FIX_LATER */ + } + + 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; + + /* + We do the check here because NDB has a worker THD which doesn't + exit until NDB is shut down. + */ + if (ref_check && plugin->ref_count) + sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.", + plugin->name.str, plugin->ref_count); +} + + +static void plugin_del(struct st_plugin_int *plugin) +{ + DBUG_ENTER("plugin_del(plugin)"); + safe_mutex_assert_owner(&LOCK_plugin); + /* Free allocated strings before deleting the plugin. */ + plugin_vars_free_values(plugin->system_vars); + hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin); + if (plugin->plugin_dl) + plugin_dl_del(&plugin->plugin_dl->dl); + plugin->state= PLUGIN_IS_FREED; + plugin_array_version++; + rw_wrlock(&LOCK_system_variables_hash); + mysql_del_sys_var_chain(plugin->system_vars); + rw_unlock(&LOCK_system_variables_hash); + free_root(&plugin->mem_root, MYF(0)); + DBUG_VOID_RETURN; +} + +#ifdef NOT_USED + +static void plugin_del(const LEX_STRING *name) +{ + struct st_plugin_int *plugin; + DBUG_ENTER("plugin_del(name)"); + if ((plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN))) + plugin_del(plugin); + DBUG_VOID_RETURN; +} + +#endif + +static void reap_plugins(void) +{ + uint count, idx; + struct st_plugin_int *plugin, **reap, **list; + + safe_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 (idx= 0; idx < count; idx++) + { + plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **); + 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; + } + } + + pthread_mutex_unlock(&LOCK_plugin); + + list= reap; + while ((plugin= *(--list))) + plugin_deinitialize(plugin, true); + + pthread_mutex_lock(&LOCK_plugin); + + while ((plugin= *(--reap))) + plugin_del(plugin); + + my_afree(reap); +} + +static void intern_plugin_unlock(LEX *lex, plugin_ref plugin) +{ + int i; + st_plugin_int *pi; + DBUG_ENTER("intern_plugin_unlock"); + + safe_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((uchar*) plugin, MYF(MY_WME)); +#endif + + DBUG_PRINT("info",("unlocking plugin, name= %s, ref_count= %d", + pi->name.str, pi->ref_count)); + 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--; + + if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count) + reap_needed= true; + + DBUG_VOID_RETURN; +} + + +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 + pthread_mutex_lock(&LOCK_plugin); + intern_plugin_unlock(lex, plugin); + reap_plugins(); + pthread_mutex_unlock(&LOCK_plugin); + DBUG_VOID_RETURN; +} + + +void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) +{ + LEX *lex= thd ? thd->lex : 0; + DBUG_ENTER("plugin_unlock_list"); + DBUG_ASSERT(list); + pthread_mutex_lock(&LOCK_plugin); + while (count--) + intern_plugin_unlock(lex, *list++); + reap_plugins(); + pthread_mutex_unlock(&LOCK_plugin); + DBUG_VOID_RETURN; +} + + +static int plugin_initialize(struct st_plugin_int *plugin) +{ + DBUG_ENTER("plugin_initialize"); + + safe_mutex_assert_owner(&LOCK_plugin); + + 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; + } + } + + plugin->state= PLUGIN_IS_READY; + + if (plugin->plugin->status_vars) + { +#ifdef FIX_LATER + /* + We have a problem right now where we can not prepend without + breaking backwards compatibility. We will fix this shortly so + that engines have "use names" and we wil use those for + CREATE TABLE, and use the plugin name then for adding automatic + variable names. + */ + SHOW_VAR array[2]= { + {plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY}, + {0, 0, SHOW_UNDEF} + }; + if (add_status_vars(array)) // add_status_vars makes a copy + goto err; +#else + add_status_vars(plugin->plugin->status_vars); // add_status_vars makes a copy +#endif /* FIX_LATER */ + } + + /* + set the plugin attribute of plugin's sys vars so they are pointing + to the active plugin + */ + if (plugin->system_vars) + { + sys_var_pluginvar *var= plugin->system_vars->cast_pluginvar(); + for (;;) + { + var->plugin= plugin; + if (!var->next) + break; + var= var->next->cast_pluginvar(); + } + } + + DBUG_RETURN(0); +err: + DBUG_RETURN(1); +} + + +extern "C" uchar *get_plugin_hash_key(const uchar *, size_t *, my_bool); +extern "C" uchar *get_bookmark_hash_key(const uchar *, size_t *, my_bool); + + +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); +} + + +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; +} + + +/* + The logic is that we first load and initialize all compiled in plugins. + From there we load up the dynamic types (assuming we have not been told to + skip this part). + + Finally we initialize everything, aka the dynamic that have yet to initialize. +*/ +int plugin_init(int *argc, char **argv, int flags) +{ + uint i; + bool def_enabled, is_myisam; + struct st_mysql_plugin **builtins; + struct st_mysql_plugin *plugin; + struct st_plugin_int tmp, *plugin_ptr, **reap; + MEM_ROOT tmp_root; + DBUG_ENTER("plugin_init"); + + if (initialized) + DBUG_RETURN(0); + + init_alloc_root(&plugin_mem_root, 4096, 4096); + init_alloc_root(&tmp_root, 4096, 4096); + + if (hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0, + get_bookmark_hash_key, NULL, HASH_UNIQUE)) + goto err; + + + pthread_mutex_init(&LOCK_plugin, MY_MUTEX_INIT_FAST); + + if (my_init_dynamic_array(&plugin_dl_array, + sizeof(struct st_plugin_dl *),16,16) || + my_init_dynamic_array(&plugin_array, + sizeof(struct st_plugin_int *),16,16)) + goto err; + + for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) + { + if (hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0, + get_plugin_hash_key, NULL, HASH_UNIQUE)) + goto err; + } + + pthread_mutex_lock(&LOCK_plugin); + + initialized= 1; + + /* + First we register builtin plugins + */ + for (builtins= mysqld_builtins; *builtins; builtins++) + { + for (plugin= *builtins; plugin->info; plugin++) + { + if (opt_ignore_builtin_innodb && + !my_strcasecmp(&my_charset_latin1, plugin->name, "InnoDB")) + continue; + /* by default, ndbcluster and federated are disabled */ + def_enabled= + my_strcasecmp(&my_charset_latin1, plugin->name, "NDBCLUSTER") != 0 && + my_strcasecmp(&my_charset_latin1, plugin->name, "FEDERATED") != 0; + bzero(&tmp, sizeof(tmp)); + tmp.plugin= plugin; + tmp.name.str= (char *)plugin->name; + tmp.name.length= strlen(plugin->name); + tmp.state= 0; + free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE)); + if (test_plugin_options(&tmp_root, &tmp, argc, argv, def_enabled)) + tmp.state= PLUGIN_IS_DISABLED; + else + tmp.state= PLUGIN_IS_UNINITIALIZED; + if (register_builtin(plugin, &tmp, &plugin_ptr)) + goto err_unlock; + + /* only initialize MyISAM and CSV at this stage */ + if (!(is_myisam= + !my_strcasecmp(&my_charset_latin1, plugin->name, "MyISAM")) && + my_strcasecmp(&my_charset_latin1, plugin->name, "CSV")) + continue; + + if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED && + plugin_initialize(plugin_ptr)) + goto err_unlock; + + /* + initialize the global default storage engine so that it may + not be null in any child thread. + */ + if (is_myisam) + { + DBUG_ASSERT(!global_system_variables.table_plugin); + global_system_variables.table_plugin= + my_intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr)); + DBUG_ASSERT(plugin_ptr->ref_count == 1); + } + } + } + + /* should now be set to MyISAM storage engine */ + DBUG_ASSERT(global_system_variables.table_plugin); + + pthread_mutex_unlock(&LOCK_plugin); + + /* Register all dynamic plugins */ + if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING)) + { + if (opt_plugin_load) + plugin_load_list(&tmp_root, argc, argv, opt_plugin_load); + if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE)) + plugin_load(&tmp_root, argc, argv); + } + + if (flags & PLUGIN_INIT_SKIP_INITIALIZATION) + goto end; + + /* + Now we initialize all remaining plugins + */ + + pthread_mutex_lock(&LOCK_plugin); + reap= (st_plugin_int **) my_alloca((plugin_array.elements+1) * sizeof(void*)); + *(reap++)= NULL; + + for (i= 0; i < plugin_array.elements; i++) + { + plugin_ptr= *dynamic_element(&plugin_array, i, struct st_plugin_int **); + if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED) + { + if (plugin_initialize(plugin_ptr)) + { + plugin_ptr->state= PLUGIN_IS_DYING; + *(reap++)= plugin_ptr; + } + } + } + + /* + Check if any plugins have to be reaped + */ + while ((plugin_ptr= *(--reap))) + { + pthread_mutex_unlock(&LOCK_plugin); + plugin_deinitialize(plugin_ptr, true); + pthread_mutex_lock(&LOCK_plugin); + plugin_del(plugin_ptr); + } + + pthread_mutex_unlock(&LOCK_plugin); + my_afree(reap); + +end: + free_root(&tmp_root, MYF(0)); + + DBUG_RETURN(0); + +err_unlock: + pthread_mutex_unlock(&LOCK_plugin); +err: + free_root(&tmp_root, MYF(0)); + DBUG_RETURN(1); +} + + +static bool register_builtin(struct st_mysql_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); +} + + +#ifdef NOT_USED_YET +/* + Register a plugin at run time. (note, this doesn't initialize a plugin) + Will be useful for embedded applications. + + SYNOPSIS + plugin_register_builtin() + thd current thread (used to store scratch data in mem_root) + plugin static plugin to install + + RETURN + false - plugin registered successfully +*/ +bool plugin_register_builtin(THD *thd, struct st_mysql_plugin *plugin) +{ + struct st_plugin_int tmp, *ptr; + bool result= true; + int dummy_argc= 0; + DBUG_ENTER("plugin_register_builtin"); + + bzero(&tmp, sizeof(tmp)); + tmp.plugin= plugin; + tmp.name.str= (char *)plugin->name; + tmp.name.length= strlen(plugin->name); + + pthread_mutex_lock(&LOCK_plugin); + rw_wrlock(&LOCK_system_variables_hash); + + if (test_plugin_options(thd->mem_root, &tmp, &dummy_argc, NULL, true)) + goto end; + tmp.state= PLUGIN_IS_UNINITIALIZED; + if ((result= register_builtin(plugin, &tmp, &ptr))) + mysql_del_sys_var_chain(tmp.system_vars); + +end: + rw_unlock(&LOCK_system_variables_hash); + pthread_mutex_unlock(&LOCK_plugin); + + DBUG_RETURN(result);; +} +#endif /* NOT_USED_YET */ + + +/* + called only by plugin_init() +*/ +static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) +{ + TABLE_LIST tables; + TABLE *table; + READ_RECORD read_record_info; + int error; + THD *new_thd; +#ifdef EMBEDDED_LIBRARY + bool table_exists; +#endif /* EMBEDDED_LIBRARY */ + DBUG_ENTER("plugin_load"); + + if (!(new_thd= new THD)) + { + sql_print_error("Can't allocate memory for plugin structures"); + delete new_thd; + DBUG_VOID_RETURN; + } + new_thd->thread_stack= (char*) &tables; + new_thd->store_globals(); + lex_start(new_thd); + new_thd->db= my_strdup("mysql", MYF(0)); + new_thd->db_length= 5; + bzero((uchar*)&tables, sizeof(tables)); + tables.alias= tables.table_name= (char*)"plugin"; + tables.lock_type= TL_READ; + tables.db= new_thd->db; + +#ifdef EMBEDDED_LIBRARY + /* + When building an embedded library, if the mysql.plugin table + does not exist, we silently ignore the missing table + */ + pthread_mutex_lock(&LOCK_open); + if (check_if_table_exists(new_thd, &tables, &table_exists)) + table_exists= FALSE; + pthread_mutex_unlock(&LOCK_open); + if (!table_exists) + goto end; +#endif /* EMBEDDED_LIBRARY */ + + if (simple_open_n_lock_tables(new_thd, &tables)) + { + DBUG_PRINT("error",("Can't open plugin table")); + sql_print_error("Can't open the mysql.plugin table. Please " + "run mysql_upgrade to create it."); + goto end; + } + table= tables.table; + init_read_record(&read_record_info, new_thd, table, NULL, 1, 0, FALSE); + table->use_all_columns(); + /* + 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 safe_mutex_assert_owner(), so we lock + the mutex here to satisfy the assert + */ + pthread_mutex_lock(&LOCK_plugin); + 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 (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG)) + sql_print_warning("Couldn't load plugin named '%s' with soname '%s'.", + str_name.c_ptr(), str_dl.c_ptr()); + free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); + } + pthread_mutex_unlock(&LOCK_plugin); + if (error > 0) + sql_print_error(ER(ER_GET_ERRNO), my_errno); + end_read_record(&read_record_info); + new_thd->version--; // Force close to free memory +end: + close_thread_tables(new_thd); + delete new_thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + DBUG_VOID_RETURN; +} + + +/* + called only by plugin_init() +*/ +static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, + const char *list) +{ + char buffer[FN_REFLEN]; + LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name; + struct st_plugin_dl *plugin_dl; + struct st_mysql_plugin *plugin; + 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 */ +#ifndef __WIN__ + case ':': /* can't use this as delimiter as it may be drive letter */ +#endif + case ';': + str->str[str->length]= '\0'; + if (str == &name) // load all plugins in named module + { + if (!name.length) + { + p--; /* reset pointer */ + continue; + } + + dl= name; + pthread_mutex_lock(&LOCK_plugin); + if ((plugin_dl= plugin_dl_add(&dl, REPORT_TO_LOG))) + { + for (plugin= plugin_dl->plugins; plugin->info; plugin++) + { + name.str= (char *) plugin->name; + name.length= strlen(name.str); + + free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); + if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG)) + goto error; + } + plugin_dl_del(&dl); // reduce ref count + } + } + else + { + free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); + pthread_mutex_lock(&LOCK_plugin); + if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG)) + goto error; + } + pthread_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; + } + default: + str->length++; + continue; + } + } + DBUG_RETURN(FALSE); +error: + pthread_mutex_unlock(&LOCK_plugin); + sql_print_error("Couldn't load plugin named '%s' with soname '%s'.", + name.str, dl.str); + DBUG_RETURN(TRUE); +} + + +void plugin_shutdown(void) +{ + uint i, count= plugin_array.elements, free_slots= 0; + struct st_plugin_int **plugins, *plugin; + struct st_plugin_dl **dl; + DBUG_ENTER("plugin_shutdown"); + + if (initialized) + { + pthread_mutex_lock(&LOCK_plugin); + + reap_needed= true; + + /* + We want to shut down plugins in a reasonable order, this will + become important when we have plugins which depend upon each other. + Circular references cannot be reaped so they are forced afterwards. + TODO: Have an additional step here to notify all active plugins that + shutdown is requested to allow plugins to deinitialize in parallel. + */ + while (reap_needed && (count= plugin_array.elements)) + { + reap_plugins(); + for (i= free_slots= 0; i < count; i++) + { + plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **); + switch (plugin->state) { + case PLUGIN_IS_READY: + plugin->state= PLUGIN_IS_DELETED; + reap_needed= true; + break; + case PLUGIN_IS_FREED: + case PLUGIN_IS_UNINITIALIZED: + free_slots++; + break; + } + } + if (!reap_needed) + { + /* + release any plugin references held. + */ + unlock_variables(NULL, &global_system_variables); + unlock_variables(NULL, &max_system_variables); + } + } + + if (count > free_slots) + sql_print_warning("Forcing shutdown of %d plugins", count - free_slots); + + plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1)); + + /* + If we have any plugins which did not die cleanly, we force shutdown + */ + for (i= 0; i < count; i++) + { + plugins[i]= *dynamic_element(&plugin_array, i, struct st_plugin_int **); + /* change the state to ensure no reaping races */ + if (plugins[i]->state == PLUGIN_IS_DELETED) + plugins[i]->state= PLUGIN_IS_DYING; + } + pthread_mutex_unlock(&LOCK_plugin); + + /* + We loop through all plugins and call deinit() if they have one. + */ + for (i= 0; i < count; i++) + if (!(plugins[i]->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_FREED | + PLUGIN_IS_DISABLED))) + { + sql_print_information("Plugin '%s' will be forced to shutdown", + plugins[i]->name.str); + /* + We are forcing deinit on plugins so we don't want to do a ref_count + check until we have processed all the plugins. + */ + plugin_deinitialize(plugins[i], false); + } + + /* + It's perfectly safe not to lock LOCK_plugin, as there're no + concurrent threads anymore. But some functions called from here + use safe_mutex_assert_owner(), so we lock the mutex to satisfy it + */ + pthread_mutex_lock(&LOCK_plugin); + + /* + We defer checking ref_counts until after all plugins are deinitialized + as some may have worker threads holding on to plugin references. + */ + for (i= 0; i < count; i++) + { + if (plugins[i]->ref_count) + sql_print_error("Plugin '%s' has ref_count=%d after shutdown.", + plugins[i]->name.str, plugins[i]->ref_count); + if (plugins[i]->state & PLUGIN_IS_UNINITIALIZED) + plugin_del(plugins[i]); + } + + /* + Now we can deallocate all memory. + */ + + cleanup_variables(NULL, &global_system_variables); + cleanup_variables(NULL, &max_system_variables); + pthread_mutex_unlock(&LOCK_plugin); + + initialized= 0; + pthread_mutex_destroy(&LOCK_plugin); + + my_afree(plugins); + } + + /* Dispose of the memory */ + + for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) + hash_free(&plugin_hash[i]); + delete_dynamic(&plugin_array); + + count= plugin_dl_array.elements; + dl= (struct st_plugin_dl **)my_alloca(sizeof(void*) * count); + for (i= 0; i < count; i++) + dl[i]= *dynamic_element(&plugin_dl_array, i, struct st_plugin_dl **); + for (i= 0; i < plugin_dl_array.elements; i++) + free_plugin_mem(dl[i]); + my_afree(dl); + delete_dynamic(&plugin_dl_array); + + hash_free(&bookmark_hash); + free_root(&plugin_mem_root, MYF(0)); + + global_variables_dynamic_size= 0; + + DBUG_VOID_RETURN; +} + + +bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl) +{ + TABLE_LIST tables; + TABLE *table; + int error, argc; + char *argv[2]; + struct st_plugin_int *tmp; + DBUG_ENTER("mysql_install_plugin"); + + bzero(&tables, sizeof(tables)); + tables.db= (char *)"mysql"; + tables.table_name= tables.alias= (char *)"plugin"; + if (check_table_access(thd, INSERT_ACL, &tables, 1, FALSE)) + DBUG_RETURN(TRUE); + + /* need to open before acquiring LOCK_plugin or it will deadlock */ + if (! (table = open_ltable(thd, &tables, TL_WRITE, 0))) + DBUG_RETURN(TRUE); + + pthread_mutex_lock(&LOCK_plugin); + rw_wrlock(&LOCK_system_variables_hash); + /* handle_options() assumes arg0 (program name) always exists */ + argv[0]= const_cast<char*>(""); // without a cast gcc emits a warning + argv[1]= 0; + argc= 1; + error= plugin_add(thd->mem_root, name, dl, &argc, argv, REPORT_TO_USER); + rw_unlock(&LOCK_system_variables_hash); + + if (error || !(tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN))) + goto err; + + if (plugin_initialize(tmp)) + { + my_error(ER_CANT_INITIALIZE_UDF, MYF(0), name->str, + "Plugin initialization function failed."); + goto deinit; + } + + /* + 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(dl->str, 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)); + goto deinit; + } + + pthread_mutex_unlock(&LOCK_plugin); + DBUG_RETURN(FALSE); +deinit: + tmp->state= PLUGIN_IS_DELETED; + reap_needed= true; + reap_plugins(); +err: + pthread_mutex_unlock(&LOCK_plugin); + DBUG_RETURN(TRUE); +} + + +bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) +{ + TABLE *table; + TABLE_LIST tables; + struct st_plugin_int *plugin; + DBUG_ENTER("mysql_uninstall_plugin"); + + bzero(&tables, sizeof(tables)); + tables.db= (char *)"mysql"; + tables.table_name= tables.alias= (char *)"plugin"; + + /* need to open before acquiring LOCK_plugin or it will deadlock */ + if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) + DBUG_RETURN(TRUE); + + pthread_mutex_lock(&LOCK_plugin); + if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN))) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str); + goto err; + } + if (!plugin->plugin_dl) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_PLUGIN_DELETE_BUILTIN, ER(WARN_PLUGIN_DELETE_BUILTIN)); + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str); + goto err; + } + + plugin->state= PLUGIN_IS_DELETED; + if (plugin->ref_count) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY)); + else + reap_needed= true; + reap_plugins(); + pthread_mutex_unlock(&LOCK_plugin); + + table->use_all_columns(); + table->field[0]->store(name->str, name->length, system_charset_info); + if (! table->file->index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + 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)); + DBUG_RETURN(TRUE); + } + } + DBUG_RETURN(FALSE); +err: + pthread_mutex_unlock(&LOCK_plugin); + DBUG_RETURN(TRUE); +} + + +bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, + int type, uint state_mask, void *arg) +{ + uint idx, total; + struct st_plugin_int *plugin, **plugins; + int version=plugin_array_version; + DBUG_ENTER("plugin_foreach_with_mask"); + + if (!initialized) + DBUG_RETURN(FALSE); + + state_mask= ~state_mask; // do it only once + + pthread_mutex_lock(&LOCK_plugin); + total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements + : plugin_hash[type].records; + /* + Do the alloca out here in case we do have a working alloca: + leaving the nested stack frame invalidates alloca allocation. + */ + plugins=(struct st_plugin_int **)my_alloca(total*sizeof(plugin)); + if (type == MYSQL_ANY_PLUGIN) + { + for (idx= 0; idx < total; idx++) + { + plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **); + plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL; + } + } + else + { + HASH *hash= plugin_hash + type; + for (idx= 0; idx < total; idx++) + { + plugin= (struct st_plugin_int *) hash_element(hash, idx); + plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL; + } + } + pthread_mutex_unlock(&LOCK_plugin); + + for (idx= 0; idx < total; idx++) + { + if (unlikely(version != plugin_array_version)) + { + pthread_mutex_lock(&LOCK_plugin); + for (uint i=idx; i < total; i++) + if (plugins[i] && plugins[i]->state & state_mask) + plugins[i]=0; + pthread_mutex_unlock(&LOCK_plugin); + } + plugin= plugins[idx]; + /* It will stop iterating on first engine error when "func" returns TRUE */ + if (plugin && func(thd, plugin_int_to_ref(plugin), arg)) + goto err; + } + + my_afree(plugins); + DBUG_RETURN(FALSE); +err: + my_afree(plugins); + DBUG_RETURN(TRUE); +} + + +/**************************************************************************** + Internal type declarations for variables support +****************************************************************************/ + +#undef MYSQL_SYSVAR_NAME +#define MYSQL_SYSVAR_NAME(name) name +#define PLUGIN_VAR_TYPEMASK 0x007f + +#define EXTRA_OPTIONS 3 /* options for: 'foo', 'plugin-foo' and NULL */ + +typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_bool_t, my_bool); +typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_bool_t, my_bool); +typedef DECLARE_MYSQL_SYSVAR_BASIC(sysvar_str_t, char *); +typedef DECLARE_MYSQL_THDVAR_BASIC(thdvar_str_t, char *); + +typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_enum_t, unsigned long); +typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_enum_t, unsigned long); +typedef DECLARE_MYSQL_SYSVAR_TYPELIB(sysvar_set_t, ulonglong); +typedef DECLARE_MYSQL_THDVAR_TYPELIB(thdvar_set_t, ulonglong); + +typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_int_t, int); +typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_long_t, long); +typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_longlong_t, longlong); +typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_uint_t, uint); +typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulong_t, ulong); +typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulonglong_t, ulonglong); + +typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_int_t, int); +typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_long_t, long); +typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_longlong_t, longlong); +typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_uint_t, uint); +typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulong_t, ulong); +typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulonglong_t, ulonglong); + +#define SET_PLUGIN_VAR_RESOLVE(opt)\ + *(mysql_sys_var_ptr_p*)&((opt)->resolve)= mysql_sys_var_ptr +typedef uchar *(*mysql_sys_var_ptr_p)(void* a_thd, int offset); + + +/**************************************************************************** + default variable data check and update functions +****************************************************************************/ + +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 *strvalue= "NULL", *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) + { + if (str) + strvalue= str; + goto err; + } + } + else + { + if (value->val_int(value, &tmp) < 0) + goto err; + if (tmp > 1) + { + llstr(tmp, buff); + strvalue= buff; + goto err; + } + result= (int) tmp; + } + *(my_bool *) save= -result; + return 0; +err: + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->name, strvalue); + return 1; +} + + +static int check_func_int(THD *thd, struct st_mysql_sys_var *var, + void *save, st_mysql_value *value) +{ + my_bool fixed; + long long tmp; + struct my_option options; + value->val_int(value, &tmp); + plugin_opt_set_limits(&options, var); + + if (var->flags & PLUGIN_VAR_UNSIGNED) + *(uint *)save= (uint) getopt_ull_limit_value((ulonglong) tmp, &options, + &fixed); + else + *(int *)save= (int) getopt_ll_limit_value(tmp, &options, &fixed); + + return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED, + var->name, (longlong) tmp); +} + + +static int check_func_long(THD *thd, struct st_mysql_sys_var *var, + void *save, st_mysql_value *value) +{ + my_bool fixed; + long long tmp; + struct my_option options; + value->val_int(value, &tmp); + plugin_opt_set_limits(&options, var); + + if (var->flags & PLUGIN_VAR_UNSIGNED) + *(ulong *)save= (ulong) getopt_ull_limit_value((ulonglong) tmp, &options, + &fixed); + else + *(long *)save= (long) getopt_ll_limit_value(tmp, &options, &fixed); + + return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED, + var->name, (longlong) tmp); +} + + +static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var, + void *save, st_mysql_value *value) +{ + my_bool fixed; + long long tmp; + struct my_option options; + value->val_int(value, &tmp); + plugin_opt_set_limits(&options, var); + + if (var->flags & PLUGIN_VAR_UNSIGNED) + *(ulonglong *)save= getopt_ull_limit_value((ulonglong) tmp, &options, + &fixed); + else + *(longlong *)save= getopt_ll_limit_value(tmp, &options, &fixed); + + return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED, + var->name, (longlong) tmp); +} + +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 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 *strvalue= "NULL", *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, 1)-1) < 0) + { + strvalue= str; + goto err; + } + } + else + { + if (value->val_int(value, &tmp)) + goto err; + if (tmp >= typelib->count) + { + llstr(tmp, buff); + strvalue= buff; + goto err; + } + result= (long) tmp; + } + *(long*)save= result; + return 0; +err: + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->name, strvalue); + return 1; +} + + +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 *strvalue= "NULL", *str; + TYPELIB *typelib; + ulonglong result; + uint error_len; + 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, ¬_used); + if (error_len) + { + strmake(buff, error, min(sizeof(buff), error_len)); + strvalue= buff; + goto err; + } + } + else + { + if (value->val_int(value, (long long *)&result)) + goto err; + if (unlikely((result >= (ULL(1) << typelib->count)) && + (typelib->count < sizeof(long)*8))) + { + llstr(result, buff); + strvalue= buff; + goto err; + } + } + *(ulonglong*)save= result; + return 0; +err: + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->name, strvalue); + return 1; +} + + +static void update_func_bool(THD *thd, struct st_mysql_sys_var *var, + void *tgt, const void *save) +{ + *(my_bool *) tgt= *(my_bool *) save ? TRUE : FALSE; +} + + +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_long(THD *thd, struct st_mysql_sys_var *var, + void *tgt, const void *save) +{ + *(long *)tgt= *(long *) save; +} + + +static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var, + void *tgt, const void *save) +{ + *(longlong *)tgt= *(ulonglong *) save; +} + + +static void update_func_str(THD *thd, struct st_mysql_sys_var *var, + void *tgt, const void *save) +{ + char *old= *(char **) tgt; + *(char **)tgt= *(char **) save; + if (var->flags & PLUGIN_VAR_MEMALLOC) + { + *(char **)tgt= my_strdup(*(char **) save, MYF(0)); + my_free(old, MYF(0)); + } +} + + +/**************************************************************************** + System Variables support +****************************************************************************/ + + +sys_var *find_sys_var(THD *thd, const char *str, uint length) +{ + sys_var *var; + sys_var_pluginvar *pi= NULL; + plugin_ref plugin; + DBUG_ENTER("find_sys_var"); + + pthread_mutex_lock(&LOCK_plugin); + rw_rdlock(&LOCK_system_variables_hash); + if ((var= intern_find_sys_var(str, length, false)) && + (pi= var->cast_pluginvar())) + { + rw_unlock(&LOCK_system_variables_hash); + LEX *lex= thd ? thd->lex : 0; + if (!(plugin= my_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 + rw_unlock(&LOCK_system_variables_hash); + pthread_mutex_unlock(&LOCK_plugin); + + /* + If the variable exists but the plugin it is associated with is not ready + then the intern_plugin_lock did not raise an error, so we do it here. + */ + if (pi && !var) + my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str); + DBUG_RETURN(var); +} + + +/* + called by register_var, construct_options and test_plugin_options. + Returns the 'bookmark' for the named variable. + LOCK_system_variables_hash should be at least read locked +*/ +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]= flags & PLUGIN_VAR_TYPEMASK; + + result= (st_bookmark*) hash_search(&bookmark_hash, + (const uchar*) varname, length - 1); + + my_afree(varname); + return result; +} + + +/* + returns a bookmark for thd-local variables, creating if neccessary. + returns null for non thd-local variables. + Requires that a write lock is obtained on LOCK_system_variables_hash +*/ +static st_bookmark *register_var(const char *plugin, const char *name, + int flags) +{ + uint length= strlen(plugin) + strlen(name) + 3, size= 0, offset, new_size; + st_bookmark *result; + char *varname, *p; + + if (!(flags & PLUGIN_VAR_THDLOCAL)) + return NULL; + + switch (flags & PLUGIN_VAR_TYPEMASK) { + case PLUGIN_VAR_BOOL: + size= sizeof(my_bool); + break; + case PLUGIN_VAR_INT: + size= sizeof(int); + break; + case PLUGIN_VAR_LONG: + case PLUGIN_VAR_ENUM: + size= sizeof(long); + break; + case PLUGIN_VAR_LONGLONG: + case PLUGIN_VAR_SET: + size= sizeof(ulonglong); + break; + case PLUGIN_VAR_STR: + size= sizeof(char*); + break; + default: + DBUG_ASSERT(0); + return NULL; + }; + + 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_mem_root, + sizeof(struct st_bookmark) + length-1); + varname[0]= flags & PLUGIN_VAR_TYPEMASK; + 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; +} + + +/* + returns a pointer to the memory which holds the thd-local variable or + a pointer to the global variable if thd==null. + If required, will sync with global variables if the requested variable + has not yet been allocated in the current thread. +*/ +static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock) +{ + DBUG_ASSERT(offset >= 0); + DBUG_ASSERT((uint)offset <= global_system_variables.dynamic_variables_head); + + if (!thd) + 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) + { + uint idx; + + rw_rdlock(&LOCK_system_variables_hash); + + 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) + pthread_mutex_lock(&LOCK_global_system_variables); + + safe_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++) + { + sys_var_pluginvar *pi; + sys_var *var; + st_bookmark *v= (st_bookmark*) hash_element(&bookmark_hash,idx); + + if (v->version <= thd->variables.dynamic_variables_version || + !(var= intern_find_sys_var(v->key + 1, v->name_len, true)) || + !(pi= var->cast_pluginvar()) || + v->key[0] != (pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK)) + continue; + + /* Here we do anything special that may be required of the data types */ + + if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR && + pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC) + { + char **pp= (char**) (thd->variables.dynamic_variables_ptr + + *(int*)(pi->plugin_var + 1)); + if ((*pp= *(char**) (global_system_variables.dynamic_variables_ptr + + *(int*)(pi->plugin_var + 1)))) + *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE)); + } + } + + if (global_lock) + pthread_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; + + rw_unlock(&LOCK_system_variables_hash); + } + return (uchar*)thd->variables.dynamic_variables_ptr + offset; +} + +static uchar *mysql_sys_var_ptr(void* a_thd, int offset) +{ + return intern_sys_var_ptr((THD *)a_thd, offset, true); +} + + +void plugin_thdvar_init(THD *thd) +{ + plugin_ref old_table_plugin= thd->variables.table_plugin; + DBUG_ENTER("plugin_thdvar_init"); + + thd->variables.table_plugin= NULL; + cleanup_variables(thd, &thd->variables); + + thd->variables= global_system_variables; + thd->variables.table_plugin= NULL; + + /* 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; + + pthread_mutex_lock(&LOCK_plugin); + thd->variables.table_plugin= + my_intern_plugin_lock(NULL, global_system_variables.table_plugin); + intern_plugin_unlock(NULL, old_table_plugin); + pthread_mutex_unlock(&LOCK_plugin); + DBUG_VOID_RETURN; +} + + +/* + Unlocks all system variables which hold a reference +*/ +static void unlock_variables(THD *thd, struct system_variables *vars) +{ + intern_plugin_unlock(NULL, vars->table_plugin); + vars->table_plugin= NULL; +} + + +/* + Frees memory used by system variables + + Unlike plugin_vars_free_values() it frees all variables of all plugins, + it's used on shutdown. +*/ +static void cleanup_variables(THD *thd, struct system_variables *vars) +{ + st_bookmark *v; + sys_var_pluginvar *pivar; + sys_var *var; + int flags; + uint idx; + + rw_rdlock(&LOCK_system_variables_hash); + for (idx= 0; idx < bookmark_hash.records; idx++) + { + v= (st_bookmark*) hash_element(&bookmark_hash, idx); + if (v->version > vars->dynamic_variables_version || + !(var= intern_find_sys_var(v->key + 1, v->name_len, true)) || + !(pivar= var->cast_pluginvar()) || + v->key[0] != (pivar->plugin_var->flags & PLUGIN_VAR_TYPEMASK)) + continue; + + flags= pivar->plugin_var->flags; + + if ((flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR && + flags & PLUGIN_VAR_THDLOCAL && flags & PLUGIN_VAR_MEMALLOC) + { + char **ptr= (char**) pivar->real_value_ptr(thd, OPT_SESSION); + my_free(*ptr, MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); + *ptr= NULL; + } + } + rw_unlock(&LOCK_system_variables_hash); + + DBUG_ASSERT(vars->table_plugin == NULL); + + my_free(vars->dynamic_variables_ptr, MYF(MY_ALLOW_ZERO_PTR)); + vars->dynamic_variables_ptr= NULL; + vars->dynamic_variables_size= 0; + vars->dynamic_variables_version= 0; +} + + +void plugin_thdvar_cleanup(THD *thd) +{ + uint idx; + plugin_ref *list; + DBUG_ENTER("plugin_thdvar_cleanup"); + + pthread_mutex_lock(&LOCK_plugin); + + unlock_variables(thd, &thd->variables); + cleanup_variables(thd, &thd->variables); + + if ((idx= thd->lex->plugins.elements)) + { + list= ((plugin_ref*) thd->lex->plugins.buffer) + idx - 1; + DBUG_PRINT("info",("unlocking %d plugins", idx)); + while ((uchar*) list >= thd->lex->plugins.buffer) + intern_plugin_unlock(NULL, *list--); + } + + reap_plugins(); + pthread_mutex_unlock(&LOCK_plugin); + + reset_dynamic(&thd->lex->plugins); + + DBUG_VOID_RETURN; +} + + +/** + @brief Free values of thread variables of a plugin. + + This must be called before a plugin is deleted. Otherwise its + variables are no longer accessible and the value space is lost. Note + that only string values with PLUGIN_VAR_MEMALLOC are allocated and + must be freed. + + @param[in] vars Chain of system variables of a plugin +*/ + +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: 0x%lx", + var->name, (long) valptr)); + my_free(*valptr, MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); + *valptr= NULL; + } + } + DBUG_VOID_RETURN; +} + + +bool sys_var_pluginvar::check_update_type(Item_result type) +{ + if (is_readonly()) + return 1; + switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) { + case PLUGIN_VAR_INT: + case PLUGIN_VAR_LONG: + case PLUGIN_VAR_LONGLONG: + return type != INT_RESULT; + case PLUGIN_VAR_STR: + return type != STRING_RESULT; + default: + return 0; + } +} + + +SHOW_TYPE sys_var_pluginvar::show_type() +{ + switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) { + case PLUGIN_VAR_BOOL: + return SHOW_MY_BOOL; + case PLUGIN_VAR_INT: + return SHOW_INT; + case PLUGIN_VAR_LONG: + return SHOW_LONG; + case PLUGIN_VAR_LONGLONG: + return SHOW_LONGLONG; + case PLUGIN_VAR_STR: + return SHOW_CHAR_PTR; + case PLUGIN_VAR_ENUM: + case PLUGIN_VAR_SET: + return SHOW_CHAR; + default: + DBUG_ASSERT(0); + return SHOW_UNDEF; + } +} + + +uchar* sys_var_pluginvar::real_value_ptr(THD *thd, enum_var_type type) +{ + 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); +} + + +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; +} + + +uchar* sys_var_pluginvar::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + uchar* result; + + result= real_value_ptr(thd, type); + + if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_ENUM) + result= (uchar*) get_type(plugin_var_typelib(), *(ulong*)result); + else if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_SET) + { + char buffer[STRING_BUFFER_USUAL_SIZE]; + String str(buffer, sizeof(buffer), system_charset_info); + TYPELIB *typelib= plugin_var_typelib(); + ulonglong mask= 1, value= *(ulonglong*) result; + uint i; + + str.length(0); + for (i= 0; i < typelib->count; i++, mask<<=1) + { + if (!(value & mask)) + continue; + str.append(typelib->type_names[i], typelib->type_lengths[i]); + str.append(','); + } + + result= (uchar*) ""; + if (str.length()) + result= (uchar*) thd->strmake(str.ptr(), str.length()-1); + } + return result; +} + + +bool sys_var_pluginvar::check(THD *thd, set_var *var) +{ + st_item_value_holder value; + DBUG_ASSERT(is_readonly() || 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.item= var->value; + + return is_readonly() || + plugin_var->check(thd, plugin_var, &var->save_result, &value); +} + + +void sys_var_pluginvar::set_default(THD *thd, enum_var_type type) +{ + const void *src; + void *tgt; + + DBUG_ASSERT(is_readonly() || plugin_var->update); + + if (is_readonly()) + return; + + pthread_mutex_lock(&LOCK_global_system_variables); + tgt= real_value_ptr(thd, type); + src= ((void **) (plugin_var + 1) + 1); + + if (plugin_var->flags & PLUGIN_VAR_THDLOCAL) + { + if (type != OPT_GLOBAL) + src= real_value_ptr(thd, OPT_GLOBAL); + else + switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) { + case PLUGIN_VAR_INT: + src= &((thdvar_uint_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_LONG: + src= &((thdvar_ulong_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_LONGLONG: + src= &((thdvar_ulonglong_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_ENUM: + src= &((thdvar_enum_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_SET: + src= &((thdvar_set_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_BOOL: + src= &((thdvar_bool_t*) plugin_var)->def_val; + break; + case PLUGIN_VAR_STR: + src= &((thdvar_str_t*) plugin_var)->def_val; + break; + default: + DBUG_ASSERT(0); + } + } + + /* thd must equal current_thd if PLUGIN_VAR_THDLOCAL flag is set */ + DBUG_ASSERT(!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) || + thd == current_thd); + + if (!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) || type == OPT_GLOBAL) + { + plugin_var->update(thd, plugin_var, tgt, src); + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + { + pthread_mutex_unlock(&LOCK_global_system_variables); + plugin_var->update(thd, plugin_var, tgt, src); + } +} + + +bool sys_var_pluginvar::update(THD *thd, set_var *var) +{ + void *tgt; + + DBUG_ASSERT(is_readonly() || plugin_var->update); + + /* thd must equal current_thd if PLUGIN_VAR_THDLOCAL flag is set */ + DBUG_ASSERT(!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) || + thd == current_thd); + + if (is_readonly()) + return 1; + + pthread_mutex_lock(&LOCK_global_system_variables); + tgt= real_value_ptr(thd, var->type); + + if (!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) || var->type == OPT_GLOBAL) + { + /* variable we are updating has global scope, so we unlock after updating */ + plugin_var->update(thd, plugin_var, tgt, &var->save_result); + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + { + pthread_mutex_unlock(&LOCK_global_system_variables); + plugin_var->update(thd, plugin_var, tgt, &var->save_result); + } + return 0; +} + + +#define OPTION_SET_LIMITS(type, options, opt) \ + options->var_type= type; \ + options->def_value= (opt)->def_val; \ + options->min_value= (opt)->min_val; \ + options->max_value= (opt)->max_val; \ + options->block_size= (long) (opt)->blk_sz + + +static 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= (ULL(1) << options->typelib->count) - 1; + break; + case PLUGIN_VAR_BOOL: + options->var_type= GET_BOOL; + options->def_value= ((sysvar_bool_t*) opt)->def_val; + 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; + /* 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_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= (ULL(1) << 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; + 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; +} + +extern "C" my_bool get_one_plugin_option(int optid, const struct my_option *, + char *); + +my_bool get_one_plugin_option(int optid __attribute__((unused)), + const struct my_option *opt, + char *argument) +{ + return 0; +} + + +static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, + my_option *options, my_bool **enabled, + bool can_disable) +{ + const char *plugin_name= tmp->plugin->name; + uint namelen= strlen(plugin_name), optnamelen; + uint buffer_length= namelen * 4 + (can_disable ? 75 : 10); + char *name= (char*) alloc_root(mem_root, buffer_length) + 1; + char *optname, *p; + int index= 0, offset= 0; + st_mysql_sys_var *opt, **plugin_option; + st_bookmark *v; + DBUG_ENTER("construct_options"); + DBUG_PRINT("plugin", ("plugin: '%s' enabled: %d can_disable: %d", + plugin_name, **enabled, can_disable)); + + /* support --skip-plugin-foo syntax */ + memcpy(name, plugin_name, namelen + 1); + my_casedn_str(&my_charset_latin1, name); + strxmov(name + namelen + 1, "plugin-", name, NullS); + /* Now we have namelen + 1 + 7 + namelen + 1 == namelen * 2 + 9. */ + + for (p= name + namelen*2 + 8; p > name; p--) + if (*p == '_') + *p= '-'; + + if (can_disable) + { + strxmov(name + namelen*2 + 10, "Enable ", plugin_name, " plugin. " + "Disable with --skip-", name," (will save memory).", NullS); + /* + Now we have namelen * 2 + 10 (one char unused) + 7 + namelen + 9 + + 20 + namelen + 20 + 1 == namelen * 4 + 67. + */ + + options[0].comment= name + namelen*2 + 10; + } + + /* + NOTE: 'name' is one char above the allocated buffer! + NOTE: This code assumes that 'my_bool' and 'char' are of same size. + */ + *((my_bool *)(name -1))= **enabled; + *enabled= (my_bool *)(name - 1); + + + options[1].name= (options[0].name= name) + namelen + 1; + options[0].id= options[1].id= 256; /* must be >255. dup id ok */ + options[0].var_type= options[1].var_type= GET_BOOL; + options[0].arg_type= options[1].arg_type= NO_ARG; + options[0].def_value= options[1].def_value= **enabled; + options[0].value= options[0].u_max_value= + options[1].value= options[1].u_max_value= (uchar**) (name - 1); + 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->flags & PLUGIN_VAR_THDLOCAL)) + continue; + if (!(register_var(name, opt->name, opt->flags))) + continue; + switch (opt->flags & PLUGIN_VAR_TYPEMASK) { + case PLUGIN_VAR_BOOL: + SET_PLUGIN_VAR_RESOLVE((thdvar_bool_t *) opt); + break; + case PLUGIN_VAR_INT: + SET_PLUGIN_VAR_RESOLVE((thdvar_int_t *) opt); + break; + case PLUGIN_VAR_LONG: + SET_PLUGIN_VAR_RESOLVE((thdvar_long_t *) opt); + break; + case PLUGIN_VAR_LONGLONG: + SET_PLUGIN_VAR_RESOLVE((thdvar_longlong_t *) opt); + break; + case PLUGIN_VAR_STR: + SET_PLUGIN_VAR_RESOLVE((thdvar_str_t *) opt); + break; + case PLUGIN_VAR_ENUM: + SET_PLUGIN_VAR_RESOLVE((thdvar_enum_t *) opt); + break; + case PLUGIN_VAR_SET: + SET_PLUGIN_VAR_RESOLVE((thdvar_set_t *) opt); + 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; + 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->name) + { + sql_print_error("Missing variable name in plugin '%s'.", + plugin_name); + DBUG_RETURN(-1); + } + + if (!(opt->flags & PLUGIN_VAR_THDLOCAL)) + { + optnamelen= strlen(opt->name); + optname= (char*) alloc_root(mem_root, namelen + optnamelen + 2); + strxmov(optname, name, "-", opt->name, NullS); + optnamelen= namelen + optnamelen + 1; + } + else + { + /* this should not fail because register_var should create entry */ + if (!(v= find_bookmark(name, 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) + continue; + + optname= (char*) memdup_root(mem_root, v->key + 1, + (optnamelen= v->name_len) + 1); + } + + /* convert '_' to '-' */ + for (p= optname; *p; p++) + if (*p == '_') + *p= '-'; + + options->name= optname; + options->comment= opt->comment; + options->app_type= opt; + options->id= (options-1)->id + 1; + + 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); + + options[1]= options[0]; + options[1].name= p= (char*) alloc_root(mem_root, optnamelen + 8); + options[1].comment= 0; // hidden + strxmov(p, "plugin-", optname, NullS); + + options+= 2; + } + + DBUG_RETURN(0); +} + + +static my_option *construct_help_options(MEM_ROOT *mem_root, + struct st_plugin_int *p) +{ + st_mysql_sys_var **opt; + my_option *opts; + my_bool dummy, can_disable; + my_bool *dummy2= &dummy; + 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); + + dummy= TRUE; /* plugin is enabled. */ + + can_disable= + my_strcasecmp(&my_charset_latin1, p->name.str, "MyISAM") && + my_strcasecmp(&my_charset_latin1, p->name.str, "MEMORY"); + + if (construct_options(mem_root, p, opts, &dummy2, can_disable)) + DBUG_RETURN(NULL); + + DBUG_RETURN(opts); +} + + +/* + SYNOPSIS + test_plugin_options() + tmp_root temporary scratch space + plugin internal plugin structure + argc user supplied arguments + argv user supplied arguments + default_enabled default plugin enable status + RETURNS: + 0 SUCCESS - plugin should be enabled/loaded + NOTE: + Requires that a write-lock is held on LOCK_system_variables_hash +*/ +static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, + int *argc, char **argv, my_bool default_enabled) +{ + struct sys_var_chain chain= { NULL, NULL }; + my_bool enabled_saved= default_enabled, can_disable; + my_bool *enabled= &default_enabled; + MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ? + &tmp->mem_root : &plugin_mem_root; + st_mysql_sys_var **opt; + my_option *opts= NULL; + char *p, *varname; + int error; + st_mysql_sys_var *o; + sys_var *v; + struct st_bookmark *var; + uint len, count= EXTRA_OPTIONS; + DBUG_ENTER("test_plugin_options"); + DBUG_ASSERT(tmp->plugin && tmp->name.str); + + for (opt= tmp->plugin->system_vars; opt && *opt; opt++) + count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */ + + can_disable= + my_strcasecmp(&my_charset_latin1, tmp->name.str, "MyISAM") && + my_strcasecmp(&my_charset_latin1, tmp->name.str, "MEMORY"); + + if (count > EXTRA_OPTIONS || (*argc > 1)) + { + 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, &enabled, can_disable)) + { + sql_print_error("Bad options for plugin '%s'.", tmp->name.str); + DBUG_RETURN(-1); + } + + error= handle_options(argc, &argv, opts, get_one_plugin_option); + (*argc)++; /* add back one for the program name */ + + if (error) + { + sql_print_error("Parsing options for plugin '%s' failed.", + tmp->name.str); + goto err; + } + } + + if (!*enabled && !can_disable) + { + sql_print_warning("Plugin '%s' cannot be disabled", tmp->name.str); + *enabled= TRUE; + } + + error= 1; + + if (*enabled) + { + for (opt= tmp->plugin->system_vars; opt && *opt; opt++) + { + if (((o= *opt)->flags & PLUGIN_VAR_NOSYSVAR)) + continue; + + if ((var= find_bookmark(tmp->name.str, o->name, o->flags))) + v= new (mem_root) sys_var_pluginvar(var->key + 1, o); + else + { + 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); + + for (p= varname; *p; p++) + if (*p == '-') + *p= '_'; + + v= new (mem_root) sys_var_pluginvar(varname, o); + } + DBUG_ASSERT(v); /* check that an object was actually constructed */ + + /* + Add to the chain of variables. + Done like this for easier debugging so that the + pointer to v is not lost on optimized builds. + */ + v->chain_sys_var(&chain); + } + if (chain.first) + { + chain.last->next = NULL; + if (mysql_add_sys_var_chain(chain.first, NULL)) + { + sql_print_error("Plugin '%s' has conflicting system variables", + tmp->name.str); + goto err; + } + tmp->system_vars= chain.first; + } + DBUG_RETURN(0); + } + + if (enabled_saved && global_system_variables.log_warnings) + sql_print_information("Plugin '%s' disabled by command line option", + tmp->name.str); +err: + if (opts) + my_cleanup_options(opts); + DBUG_RETURN(error); +} + + +/**************************************************************************** + Help Verbose text with Plugin System Variables +****************************************************************************/ + +static int option_cmp(my_option *a, my_option *b) +{ + return my_strcasecmp(&my_charset_latin1, a->name, b->name); +} + + +void my_print_help_inc_plugins(my_option *main_options, uint size) +{ + DYNAMIC_ARRAY all_options; + struct st_plugin_int *p; + MEM_ROOT mem_root; + my_option *opt; + + init_alloc_root(&mem_root, 4096, 4096); + my_init_dynamic_array(&all_options, sizeof(my_option), size, size/4); + + if (initialized) + for (uint idx= 0; idx < plugin_array.elements; idx++) + { + p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **); + + if (!p->plugin->system_vars || + !(opt= construct_help_options(&mem_root, p))) + continue; + + /* Only options with a non-NULL comment are displayed in help text */ + for (;opt->id; opt++) + if (opt->comment) + insert_dynamic(&all_options, (uchar*) opt); + } + + for (;main_options->id; main_options++) + insert_dynamic(&all_options, (uchar*) main_options); + + sort_dynamic(&all_options, (qsort_cmp) option_cmp); + + /* main_options now points to the empty option terminator */ + insert_dynamic(&all_options, (uchar*) main_options); + + my_print_help((my_option*) all_options.buffer); + my_print_variables((my_option*) all_options.buffer); + + delete_dynamic(&all_options); + free_root(&mem_root, MYF(0)); +} + |