diff options
Diffstat (limited to 'sql/sql_plugin.cc')
-rw-r--r-- | sql/sql_plugin.cc | 573 |
1 files changed, 340 insertions, 233 deletions
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index a90c7558045..6089745d85e 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2005, 2018, Oracle and/or its affiliates. - Copyright (c) 2010, 2018, MariaDB + Copyright (c) 2010, 2018, MariaDB Corporation 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 @@ -15,9 +15,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ +#include "sql_plugin.h" // Includes my_global.h #include "sql_priv.h" // SHOW_MY_BOOL #include "unireg.h" -#include "my_global.h" // REQUIRED by m_string.h #include "sql_class.h" // set_var.h: THD #include "sys_vars_shared.h" #include "sql_locale.h" @@ -53,8 +53,8 @@ static TYPELIB global_plugin_typelib= { array_elements(global_plugin_typelib_names)-1, "", global_plugin_typelib_names, NULL }; - -char *opt_plugin_load= NULL; +static I_List<i_string> opt_plugin_load_list; +I_List<i_string> *opt_plugin_load_list_ptr= &opt_plugin_load_list; char *opt_plugin_dir_ptr; char opt_plugin_dir[FN_REFLEN]; ulong plugin_maturity; @@ -196,6 +196,8 @@ static MEM_ROOT plugin_mem_root; static bool reap_needed= false; static bool initialized= 0; +ulong dlopen_count; + /* write-lock on LOCK_system_variables_hash is required before modifying @@ -255,15 +257,6 @@ class sys_var_pluginvar: public sys_var public: struct st_plugin_int *plugin; struct st_mysql_sys_var *plugin_var; - /** - variable name from whatever is hard-coded in the plugin source - and doesn't have pluginname- prefix is replaced by an allocated name - with a plugin prefix. When plugin is uninstalled we need to restore the - pointer to point to the hard-coded value, because plugin may be - installed/uninstalled many times without reloading the shared object. - */ - const char *orig_pluginvar_name; - static void *operator new(size_t size, MEM_ROOT *mem_root) { return (void*) alloc_root(mem_root, size); } static void operator delete(void *ptr_arg,size_t size) @@ -277,8 +270,7 @@ public: (plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0), 0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0, VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL), - plugin(plugin_arg), plugin_var(plugin_var_arg), - orig_pluginvar_name(plugin_var_arg->name) + plugin(plugin_arg), plugin_var(plugin_var_arg) { plugin_var->name= name_arg; } sys_var_pluginvar *cast_pluginvar() { return this; } bool check_update_type(Item_result type); @@ -308,9 +300,7 @@ static bool register_builtin(struct st_maria_plugin *, 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 restore_pluginvar_names(sys_var *first); -static void plugin_opt_set_limits(struct my_option *, - const struct st_mysql_sys_var *); +static void restore_ptr_backup(uint n, st_ptr_backup *backup); static void intern_plugin_unlock(LEX *lex, plugin_ref plugin); static void reap_plugins(void); @@ -485,9 +475,16 @@ static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *) #endif /* HAVE_DLOPEN */ -static inline void free_plugin_mem(struct st_plugin_dl *p) +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 @@ -518,7 +515,6 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl, /* Determine interface version */ if (!sym) { - free_plugin_mem(plugin_dl); report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym); DBUG_RETURN(TRUE); } @@ -528,15 +524,13 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl, if (plugin_dl->mysqlversion < min_plugin_interface_version || (plugin_dl->mysqlversion >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8)) { - free_plugin_mem(plugin_dl); - report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, + 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))) { - free_plugin_mem(plugin_dl); report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym); DBUG_RETURN(TRUE); } @@ -567,7 +561,6 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl, MYF(MY_ZEROFILL|MY_WME)); if (!cur) { - free_plugin_mem(plugin_dl); report_error(report, ER_OUTOFMEMORY, static_cast<int>(plugin_dl->dl.length)); DBUG_RETURN(TRUE); @@ -642,7 +635,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, Actually this branch impossible because in case of absence of maria version we try mysql version. */ - free_plugin_mem(plugin_dl); report_error(report, ER_CANT_FIND_DL_ENTRY, maria_plugin_interface_version_sym); DBUG_RETURN(TRUE); @@ -653,7 +645,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, if (plugin_dl->mariaversion < min_maria_plugin_interface_version || (plugin_dl->mariaversion >> 8) > (MARIA_PLUGIN_INTERFACE_VERSION >> 8)) { - free_plugin_mem(plugin_dl); report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, "plugin interface version mismatch"); DBUG_RETURN(TRUE); @@ -661,7 +652,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, /* Find plugin declarations */ if (!(sym= dlsym(plugin_dl->handle, maria_plugin_declarations_sym))) { - free_plugin_mem(plugin_dl); report_error(report, ER_CANT_FIND_DL_ENTRY, maria_plugin_declarations_sym); DBUG_RETURN(TRUE); } @@ -675,7 +665,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, sizeof_st_plugin= *(int *)sym; else { - free_plugin_mem(plugin_dl); report_error(report, ER_CANT_FIND_DL_ENTRY, maria_sizeof_st_plugin_sym); DBUG_RETURN(TRUE); } @@ -693,7 +682,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, MYF(MY_ZEROFILL|MY_WME)); if (!cur) { - free_plugin_mem(plugin_dl); report_error(report, ER_OUTOFMEMORY, static_cast<int>(plugin_dl->dl.length)); DBUG_RETURN(TRUE); @@ -706,7 +694,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, for (i=0; (old= (struct st_maria_plugin *)(ptr + i * sizeof_st_plugin))->info; i++) - memcpy(cur + i, old, min(sizeof(cur[i]), sizeof_st_plugin)); + memcpy(cur + i, old, MY_MIN(sizeof(cur[i]), sizeof_st_plugin)); sym= cur; plugin_dl->allocated= true; @@ -725,11 +713,13 @@ 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, i; - struct st_plugin_dl *tmp, plugin_dl; + 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. @@ -767,8 +757,9 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (*errmsg == ' ') errmsg++; } report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, errmsg); - DBUG_RETURN(0); + goto ret; } + dlopen_count++; /* Checks which plugin interface present and reads info */ if (!(sym= dlsym(plugin_dl.handle, maria_plugin_interface_version_sym))) @@ -778,12 +769,12 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) plugin_interface_version_sym), dlpath, report)) - DBUG_RETURN(0); + goto ret; } else { if (read_maria_plugin_info(&plugin_dl, sym, dlpath, report)) - DBUG_RETURN(0); + goto ret; } /* link the services in */ @@ -791,7 +782,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) { if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name))) { - uint ver= (uint)(intptr)*(void**)sym; + void **ptr= (void **)sym; + uint ver= (uint)(intptr)*ptr; if (ver > list_of_services[i].version || (ver >> 8) < (list_of_services[i].version >> 8)) { @@ -800,20 +792,33 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) "service '%s' interface version mismatch", list_of_services[i].name); report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf); - DBUG_RETURN(0); + goto ret; } - *(void**)sym= list_of_services[i].service; + 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)))) { - free_plugin_mem(&plugin_dl); report_error(report, ER_OUTOFMEMORY, static_cast<int>(plugin_dl.dl.length)); - DBUG_RETURN(0); + 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, @@ -822,12 +827,17 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) /* Add this dll to array */ if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl))) { - free_plugin_mem(&plugin_dl); report_error(report, ER_OUTOFMEMORY, static_cast<int>(sizeof(struct st_plugin_dl))); - DBUG_RETURN(0); + 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"); @@ -836,34 +846,23 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) } -static void plugin_dl_del(const LEX_STRING *dl) +static void plugin_dl_del(struct st_plugin_dl *plugin_dl) { -#ifdef HAVE_DLOPEN - uint i; DBUG_ENTER("plugin_dl_del"); + if (!plugin_dl) + DBUG_VOID_RETURN; + mysql_mutex_assert_owner(&LOCK_plugin); - for (i= 0; i < plugin_dl_array.elements; i++) + /* Do not remove this element, unless no other plugin uses this dll. */ + if (! --plugin_dl->ref_count) { - 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; - } + free_plugin_mem(plugin_dl); + bzero(plugin_dl, sizeof(struct st_plugin_dl)); } + DBUG_VOID_RETURN; -#endif } @@ -929,7 +928,8 @@ SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type) static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc, uint state_mask= PLUGIN_IS_READY | - PLUGIN_IS_UNINITIALIZED) + PLUGIN_IS_UNINITIALIZED | + PLUGIN_IS_DELETED) { st_plugin_int *pi= plugin_ref_to_int(rc); DBUG_ENTER("intern_plugin_lock"); @@ -1052,13 +1052,15 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin) static bool plugin_add(MEM_ROOT *tmp_root, const LEX_STRING *name, LEX_STRING *dl, int report) { - struct st_plugin_int tmp; + struct st_plugin_int tmp, *maybe_dupe; struct st_maria_plugin *plugin; - uint oks= 0, errs= 0; + 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_UDF_EXISTS, name->str); + report_error(report, ER_PLUGIN_INSTALLED, name->str); DBUG_RETURN(TRUE); } /* Clear the whole struct to catch future extensions. */ @@ -1080,9 +1082,17 @@ static bool plugin_add(MEM_ROOT *tmp_root, (const uchar *)tmp.name.str, tmp.name.length)) continue; // plugin name doesn't match - if (!name->str && plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)) + 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] || @@ -1118,7 +1128,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, 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); + init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096, MYF(0)); if (name->str) DBUG_RETURN(FALSE); // all done @@ -1133,11 +1143,13 @@ err: break; } - if (errs == 0 && oks == 0) // no plugin was found + 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(dl); - DBUG_RETURN(errs > 0 || oks == 0); + plugin_dl_del(tmp.plugin_dl); + DBUG_RETURN(errs > 0 || oks + dupes == 0); } @@ -1152,22 +1164,21 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) if (plugin->plugin->status_vars) { -#ifdef FIX_LATER - /** - @todo - unfortunately, status variables were introduced without a - pluginname_ namespace, that is pluginname_ was not added automatically - to status variable names. It should be fixed together with the next - incompatible API change. + /* + 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 support MySQL style too. */ - SHOW_VAR array[2]= { + 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} }; - remove_status_vars(array); -#else - remove_status_vars(plugin->plugin->status_vars); -#endif /* FIX_LATER */ + 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]) @@ -1189,10 +1200,6 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) } plugin->state= PLUGIN_IS_UNINITIALIZED; - /* maintain the obsolete @@have_innodb variable */ - if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB")) - have_innodb= SHOW_OPTION_DISABLED; - /* We do the check here because NDB has a worker THD which doesn't exit until NDB is shut down. @@ -1201,7 +1208,7 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.", plugin->name.str, plugin->ref_count); - restore_pluginvar_names(plugin->system_vars); + mysql_del_sys_var_chain(plugin->system_vars); } static void plugin_del(struct st_plugin_int *plugin) @@ -1210,9 +1217,9 @@ static void plugin_del(struct st_plugin_int *plugin) 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); my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin); - if (plugin->plugin_dl) - plugin_dl_del(&plugin->plugin_dl->dl); + plugin_dl_del(plugin->plugin_dl); plugin->state= PLUGIN_IS_FREED; free_root(&plugin->mem_root, MYF(0)); DBUG_VOID_RETURN; @@ -1398,40 +1405,33 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *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. + 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 array[2]= { + 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 (add_status_vars(array)) // add_status_vars makes a copy - goto err; -#else - if (add_status_vars(plugin->plugin->status_vars)) + if (strncasecmp(show_vars->name, plugin->name.str, plugin->name.length)) + show_vars= tmp_array; + + if (add_status_vars(show_vars)) goto err; -#endif /* FIX_LATER */ } ret= 0; err: if (ret) - restore_pluginvar_names(plugin->system_vars); + mysql_del_sys_var_chain(plugin->system_vars); mysql_mutex_lock(&LOCK_plugin); plugin->state= state; - /* maintain the obsolete @@have_innodb variable */ - if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB")) - have_innodb= state & PLUGIN_IS_READY ? SHOW_OPTION_YES - : SHOW_OPTION_DISABLED; - DBUG_RETURN(ret); } @@ -1514,13 +1514,15 @@ int plugin_init(int *argc, char **argv, int flags) if (initialized) DBUG_RETURN(0); + dlopen_count =0; + #ifdef HAVE_PSI_INTERFACE init_plugin_psi_keys(); #endif - init_alloc_root(&plugin_mem_root, 4096, 4096); - init_alloc_root(&plugin_vars_mem_root, 4096, 4096); - init_alloc_root(&tmp_root, 4096, 4096); + 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, 16, 0, 0, get_bookmark_hash_key, NULL, HASH_UNIQUE)) @@ -1530,9 +1532,9 @@ int plugin_init(int *argc, char **argv, int flags) mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST); if (my_init_dynamic_array(&plugin_dl_array, - sizeof(struct st_plugin_dl *),16,16) || + sizeof(struct st_plugin_dl *), 16, 16, MYF(0)) || my_init_dynamic_array(&plugin_array, - sizeof(struct st_plugin_int *),16,16)) + sizeof(struct st_plugin_int *), 16, 16, MYF(0))) goto err; for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) @@ -1543,8 +1545,8 @@ int plugin_init(int *argc, char **argv, int flags) } /* prepare debug_sync service */ - DBUG_ASSERT(strcmp(list_of_services[5].name, "debug_sync_service") == 0); - list_of_services[5].service= *(void**)&debug_sync_C_callback_ptr; + DBUG_ASSERT(strcmp(list_of_services[4].name, "debug_sync_service") == 0); + list_of_services[4].service= *(void**)&debug_sync_C_callback_ptr; mysql_mutex_lock(&LOCK_plugin); @@ -1553,6 +1555,9 @@ int plugin_init(int *argc, char **argv, int flags) /* 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) @@ -1632,10 +1637,19 @@ int plugin_init(int *argc, char **argv, int flags) /* Register all dynamic plugins */ if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING)) { - if (opt_plugin_load) - plugin_load_list(&tmp_root, opt_plugin_load); + 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)) + { + if (global_system_variables.log_warnings >= 9) + sql_print_information("Initializing installed plugins"); plugin_load(&tmp_root); + } } /* @@ -1718,40 +1732,27 @@ static bool register_builtin(struct st_maria_plugin *plugin, */ static void plugin_load(MEM_ROOT *tmp_root) { - THD thd; TABLE_LIST tables; TABLE *table; READ_RECORD read_record_info; int error; - THD *new_thd= &thd; + THD *new_thd= new THD; bool result; -#ifdef EMBEDDED_LIBRARY - No_such_table_error_handler error_handler; -#endif /* EMBEDDED_LIBRARY */ DBUG_ENTER("plugin_load"); 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*) &thd.net, sizeof(thd.net)); + bzero((char*) &new_thd->net, sizeof(new_thd->net)); tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ); - -#ifdef EMBEDDED_LIBRARY - /* - When building an embedded library, if the mysql.plugin table - does not exist, we silently ignore the missing table - */ - new_thd->push_internal_handler(&error_handler); -#endif /* EMBEDDED_LIBRARY */ + tables.open_strategy= TABLE_LIST:: IF_EMBEDDED(OPEN_IF_EXISTS, OPEN_NORMAL); result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT); -#ifdef EMBEDDED_LIBRARY - new_thd->pop_internal_handler(); - if (error_handler.safely_trapped_errors()) + table= tables.table; + if (IF_EMBEDDED(!table, false)) goto end; -#endif /* EMBEDDED_LIBRARY */ if (result) { @@ -1763,7 +1764,7 @@ static void plugin_load(MEM_ROOT *tmp_root) sql_print_warning("Could not open mysql.plugin table. Some options may be missing from the help text"); goto end; } - table= tables.table; + if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 0, FALSE)) { sql_print_error("Could not initialize init_read_record; Plugins not " @@ -1791,20 +1792,17 @@ static void plugin_load(MEM_ROOT *tmp_root) the mutex here to satisfy the assert */ mysql_mutex_lock(&LOCK_plugin); - if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG)) - sql_print_warning("Couldn't load plugin named '%s' with soname '%s'.", - str_name.c_ptr(), str_dl.c_ptr()); + 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(ER_GET_ERRNO), my_errno); + sql_print_error(ER(ER_GET_ERRNO), my_errno, table->file->table_type()); end_read_record(&read_record_info); table->m_needs_reopen= TRUE; // Force close to free memory close_mysql_tables(new_thd); end: - /* Remember that we don't have a THD */ - my_pthread_setspecific_ptr(THR_THD, 0); + delete new_thd; DBUG_VOID_RETURN; } @@ -2051,7 +2049,7 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name, if (tmp->state == PLUGIN_IS_DISABLED) { if (global_system_variables.log_warnings) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF), name->str, "Plugin is disabled"); } @@ -2089,14 +2087,8 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, char **argv=orig_argv; DBUG_ENTER("mysql_install_plugin"); - if (opt_noacl) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); - DBUG_RETURN(TRUE); - } - tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE); - if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) + if (!opt_noacl && check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); /* need to open before acquiring LOCK_plugin or it will deadlock */ @@ -2190,7 +2182,7 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name) plugin->state= PLUGIN_IS_DELETED; if (plugin->ref_count) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY)); else reap_needed= true; @@ -2231,15 +2223,9 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name, bool error= false; DBUG_ENTER("mysql_uninstall_plugin"); - if (opt_noacl) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); - DBUG_RETURN(TRUE); - } - tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE); - if (check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE)) + if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); /* need to open before acquiring LOCK_plugin or it will deadlock */ @@ -2366,6 +2352,74 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, } +static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl, + st_maria_plugin *plug, + plugin_foreach_func *func, void *arg) +{ + for (; plug->name; plug++) + { + st_plugin_int tmp, *plugin; + + tmp.name.str= const_cast<char*>(plug->name); + tmp.name.length= strlen(plug->name); + tmp.plugin= plug; + tmp.plugin_dl= plugin_dl; + + mysql_mutex_lock(&LOCK_plugin); + if ((plugin= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)) && + plugin->plugin == plug) + + { + tmp.state= plugin->state; + tmp.load_option= plugin->load_option; + } + else + { + tmp.state= PLUGIN_IS_FREED; + tmp.load_option= PLUGIN_OFF; + } + mysql_mutex_unlock(&LOCK_plugin); + + plugin= &tmp; + if (func(thd, plugin_int_to_ref(plugin), arg)) + return 1; + } + return 0; +} + +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; +} + + /**************************************************************************** Internal type declarations for variables support ****************************************************************************/ @@ -2865,17 +2919,6 @@ static st_bookmark *register_var(const char *plugin, const char *name, return result; } -static void restore_pluginvar_names(sys_var *first) -{ - mysql_del_sys_var_chain(first); - for (sys_var *var= first; var; var= var->next) - { - sys_var_pluginvar *pv= var->cast_pluginvar(); - pv->plugin_var->name= pv->orig_pluginvar_name; - } -} - - /* returns a pointer to the memory which holds the thd-local variable or a pointer to the global variable if thd==null. @@ -2891,8 +2934,6 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock) if (!thd) DBUG_RETURN((uchar*) global_system_variables.dynamic_variables_ptr + offset); - mysql_mutex_assert_not_owner(&LOCK_open); - /* dynamic_variables_head points to the largest valid offset */ @@ -2959,10 +3000,10 @@ 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; @@ -3275,7 +3316,7 @@ bool sys_var_pluginvar::session_update(THD *thd, set_var *var) mysql_mutex_unlock(&LOCK_global_system_variables); plugin_var->update(thd, plugin_var, tgt, src); - + return false; } @@ -3351,9 +3392,8 @@ bool sys_var_pluginvar::global_update(THD *thd, set_var *var) options->max_value= getopt_double2ulonglong((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) +void plugin_opt_set_limits(struct my_option *options, + const struct st_mysql_sys_var *opt) { options->sub_size= 0; @@ -3459,17 +3499,6 @@ static void plugin_opt_set_limits(struct my_option *options, 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; -} - - /** Creates a set of my_option objects associated with a specified plugin- handle. @@ -3709,7 +3738,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, continue; } - optname= (char*) memdup_root(mem_root, v->key + 1, + optname= (char*) memdup_root(mem_root, v->key + 1, (optnamelen= v->name_len) + 1); } @@ -3765,7 +3794,7 @@ static my_option *construct_help_options(MEM_ROOT *mem_root, to get the correct (not double-prefixed) help text. We won't need @@sysvars anymore and don't care about their proper names. */ - restore_pluginvar_names(p->system_vars); + restore_ptr_backup(p->nbackups, p->ptr_backup); if (construct_options(mem_root, p, opts)) DBUG_RETURN(NULL); @@ -3810,6 +3839,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, sys_var *v __attribute__((unused)); struct st_bookmark *var; uint len, count= EXTRA_OPTIONS; + st_ptr_backup *tmp_backup= 0; DBUG_ENTER("test_plugin_options"); DBUG_ASSERT(tmp->plugin && tmp->name.str); @@ -3880,60 +3910,87 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, plugin_name= tmp->name; error= 1; - for (opt= tmp->plugin->system_vars; opt && *opt; opt++) + + if (tmp->plugin->system_vars) { - st_mysql_sys_var *o= *opt; + for (len=0, opt= tmp->plugin->system_vars; *opt; len++, opt++) /* no-op */; + tmp_backup= (st_ptr_backup *)my_alloca(len * sizeof(tmp_backup[0])); + DBUG_ASSERT(tmp->nbackups == 0); + DBUG_ASSERT(tmp->ptr_backup == 0); - /* - 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 - freed() 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 && - ((o->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_NOCMDOPT | - PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR)) + for (opt= tmp->plugin->system_vars; *opt; opt++) { - sysvar_str_t* str= (sysvar_str_t *)o; - if (*str->value) - *str->value= strdup_root(mem_root, *str->value); - } + st_mysql_sys_var *o= *opt; - var= find_bookmark(plugin_name.str, o->name, o->flags); - if (o->flags & PLUGIN_VAR_NOSYSVAR) - continue; - if (var) - v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o, tmp); - else + /* + 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 + freed() 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 && + ((o->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_NOCMDOPT | + PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR)) + { + sysvar_str_t* str= (sysvar_str_t *)o; + if (*str->value) + *str->value= strdup_root(mem_root, *str->value); + } + + var= find_bookmark(plugin_name.str, o->name, o->flags); + if (o->flags & PLUGIN_VAR_NOSYSVAR) + continue; + tmp_backup[tmp->nbackups++].save(&o->name); + if (var) + v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o, tmp); + else + { + len= plugin_name.length + strlen(o->name) + 2; + varname= (char*) alloc_root(mem_root, len); + strxmov(varname, plugin_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, o, tmp); + } + DBUG_ASSERT(v); /* check that an object was actually constructed */ + } /* end for */ + + if (tmp->nbackups) { - len= plugin_name.length + strlen(o->name) + 2; - varname= (char*) alloc_root(mem_root, len); - strxmov(varname, plugin_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, o, tmp); + 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); + goto err; + } + memcpy(tmp->ptr_backup, tmp_backup, bytes); } - DBUG_ASSERT(v); /* check that an object was actually constructed */ - } /* end for */ - if (chain.first) - { - chain.last->next = NULL; - if (mysql_add_sys_var_chain(chain.first)) + + if (chain.first) { - sql_print_error("Plugin '%s' has conflicting system variables", - tmp->name.str); - goto err; + 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; } - tmp->system_vars= chain.first; + my_afree(tmp_backup); } + DBUG_RETURN(0); - + err: + if (tmp_backup) + my_afree(tmp_backup); if (opts) my_cleanup_options(opts); DBUG_RETURN(error); @@ -3966,3 +4023,53 @@ void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root) insert_dynamic(options, (uchar*) opt); } } + + +/** + Returns a sys_var corresponding to a particular MYSQL_SYSVAR(...) +*/ +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; +} + +/* + On dlclose() we need to restore values of all symbols that we've modified in + the DSO. The reason is - the DSO might not actually be unloaded, so on the + next dlopen() these symbols will have old values, they won't be + reinitialized. + + Perhaps, there can be many reason, why a DSO won't be unloaded. Strictly + speaking, it's implementation defined whether to unload an unused DSO or to + keep it in memory. + + In particular, this happens for some plugins: In 2009 a new ELF stub was + introduced, see Ulrich Drepper's email "Unique symbols for C++" + http://www.redhat.com/archives/posix-c++-wg/2009-August/msg00002.html + + DSO that has objects with this stub (STB_GNU_UNIQUE) cannot be unloaded + (this is mentioned in the email, see the url above). + + These "unique" objects are, for example, static variables in templates, + in inline functions, in classes. So any DSO that uses them can + only be loaded once. And because Boost has them, any DSO that uses Boost + almost certainly cannot be unloaded. + + To know whether a particular DSO has these objects, one can use + + readelf -s /path/to/plugin.so|grep UNIQUE + + There's nothing we can do about it, but to reset the DSO to its initial + state before dlclose(). +*/ +static void restore_ptr_backup(uint n, st_ptr_backup *backup) +{ + while (n--) + (backup++)->restore(); +} |