summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Botchkov <holyfoot@askmonty.org>2020-08-11 00:01:53 +0400
committerAlexey Botchkov <holyfoot@askmonty.org>2020-08-11 00:01:53 +0400
commit19bdb03e97d6f760699cd718e11f71e01c268f5e (patch)
tree3399b856c7bc246e3faec3cb1aad40badac8764a
parent0041dacc1b8e85e1958355d1cfdc36055b05a884 (diff)
downloadmariadb-git-bb-mdev21211-hf.tar.gz
MDEV-21211 LOCK_plugin optimization.bb-mdev21211-hf
Use Apc_target calls to 'notify' threads about changes in plugins.
-rw-r--r--sql/sql_explain.cc26
-rw-r--r--sql/sql_explain.h8
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_plugin.cc421
-rw-r--r--sql/sql_plugin.h5
-rw-r--r--sql/sql_select.cc2
-rw-r--r--sql/sql_show.cc6
-rw-r--r--sql/sql_tvc.cc2
9 files changed, 234 insertions, 242 deletions
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 645c1618a8a..f45751d86c5 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -40,10 +40,10 @@ const char *pushed_select_text= "PUSHED SELECT";
static void write_item(Json_writer *writer, Item *item);
static void append_item_to_str(String *out, Item *item);
-Explain_query::Explain_query(THD *thd_arg, MEM_ROOT *root) :
+Explain_query::Explain_query(THD *thd, MEM_ROOT *root) :
mem_root(root), upd_del_plan(NULL), insert_plan(NULL),
- unions(root), selects(root), thd(thd_arg), apc_enabled(false),
- operations(0)
+ unions(root), selects(root), query_plan_ready(false),
+ m_thd(thd), operations(0)
{
}
@@ -62,9 +62,6 @@ static void print_json_array(Json_writer *writer,
Explain_query::~Explain_query()
{
- if (apc_enabled)
- thd->apc_target.disable();
-
delete upd_del_plan;
delete insert_plan;
uint i;
@@ -139,24 +136,17 @@ void Explain_query::add_node(Explain_node *node)
void Explain_query::add_insert_plan(Explain_insert *insert_plan_arg)
{
insert_plan= insert_plan_arg;
- query_plan_ready();
+ query_plan_set_ready();
}
void Explain_query::add_upd_del_plan(Explain_update *upd_del_plan_arg)
{
upd_del_plan= upd_del_plan_arg;
- query_plan_ready();
+ query_plan_set_ready();
}
-void Explain_query::query_plan_ready()
-{
- if (!apc_enabled)
- thd->apc_target.enable();
- apc_enabled= true;
-}
-
/*
Send EXPLAIN output to the client.
*/
@@ -237,9 +227,9 @@ void Explain_query::print_explain_json(select_result_sink *output,
CHARSET_INFO *cs= system_charset_info;
List<Item> item_list;
const String *buf= writer.output.get_string();
- item_list.push_back(new (thd->mem_root)
- Item_string(thd, buf->ptr(), buf->length(), cs),
- thd->mem_root);
+ item_list.push_back(new (m_thd->mem_root)
+ Item_string(m_thd, buf->ptr(), buf->length(), cs),
+ m_thd->mem_root);
output->send_data(item_list);
}
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index ce3f3ef06e1..c9ad0ac4e0c 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -482,7 +482,8 @@ public:
/* If true, at least part of EXPLAIN can be printed */
bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; }
- void query_plan_ready();
+ void query_plan_set_ready() { query_plan_ready= true; }
+ bool query_plan_is_ready() const { return query_plan_ready; }
MEM_ROOT *mem_root;
@@ -496,9 +497,10 @@ private:
Dynamic_array<Explain_union*> unions;
Dynamic_array<Explain_select*> selects;
+
+ bool query_plan_ready;
+ THD *m_thd; //thd->mem_root to be used in print_explain_json
- THD *thd; // for APC start/stop
- bool apc_enabled;
/*
Debugging aid: count how many times add_node() was called. Ideally, it
should be one, we currently allow O(1) query plan saves for each
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index f8b93abd4ae..b174b88bd6e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -5221,7 +5221,7 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
output->add_node(eu);
if (eu->get_select_id() == 1)
- output->query_plan_ready();
+ output->query_plan_set_ready();
return 0;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index bf33148811e..1f38c55b922 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1348,10 +1348,10 @@ bool do_command(THD *thd)
my_net_set_read_timeout(net, thd->variables.net_read_timeout);
DBUG_ASSERT(packet_length);
- DBUG_ASSERT(!thd->apc_target.is_enabled());
+ thd->apc_target.enable();
return_value= dispatch_command(command, thd, packet+1,
(uint) (packet_length-1), FALSE, FALSE);
- DBUG_ASSERT(!thd->apc_target.is_enabled());
+ thd->apc_target.disable();
out:
thd->lex->restore_set_statement_var();
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 981420f03ae..84438e3ff70 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -217,17 +217,28 @@ static struct
#include "sql_plugin_services.ic"
/*
- 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.
+ A mutex LOCK_plugin must be acquired when we change the
+ new_plugins_state content and when we swap the global_plugins_state
+ and the new_plugins_state.
*/
mysql_mutex_t LOCK_plugin;
+struct st_plugins_state
+{
+ DYNAMIC_ARRAY m_array;
+ HASH m_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
+#ifndef DBUG_OOF
+ uint m_ref_counter;
+#endif
+};
+
static DYNAMIC_ARRAY plugin_dl_array;
-static DYNAMIC_ARRAY plugin_array;
-static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
static MEM_ROOT plugin_mem_root;
static bool reap_needed= false;
volatile int global_plugin_version= 1;
+static st_plugins_state plugins_states[2];
+st_plugins_state *global_plugins_state= plugins_states;
+static st_plugins_state *new_plugins_state= plugins_states + 1;
+
static bool initialized= 0;
ulong dlopen_count;
@@ -871,7 +882,8 @@ static void plugin_dl_del(struct st_plugin_dl *plugin_dl)
}
-static struct st_plugin_int *plugin_find_internal(const LEX_CSTRING *name,
+static struct st_plugin_int *plugin_find_internal(st_plugins_state *st,
+ const LEX_CSTRING *name,
int type)
{
uint i;
@@ -879,21 +891,19 @@ static struct st_plugin_int *plugin_find_internal(const LEX_CSTRING *name,
if (! initialized)
DBUG_RETURN(0);
- mysql_mutex_assert_owner(&LOCK_plugin);
-
if (type == MYSQL_ANY_PLUGIN)
{
for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
{
struct st_plugin_int *plugin= (st_plugin_int *)
- my_hash_search(&plugin_hash[i], (const uchar *)name->str, name->length);
+ my_hash_search(&st->m_hash[i], (const uchar *)name->str, name->length);
if (plugin)
DBUG_RETURN(plugin);
}
}
else
DBUG_RETURN((st_plugin_int *)
- my_hash_search(&plugin_hash[type], (const uchar *)name->str,
+ my_hash_search(&st->m_hash[type], (const uchar *)name->str,
name->length));
DBUG_RETURN(0);
}
@@ -903,32 +913,20 @@ static SHOW_COMP_OPTION plugin_status(const LEX_CSTRING *name, int type)
{
SHOW_COMP_OPTION rc= SHOW_OPTION_NO;
struct st_plugin_int *plugin;
- DBUG_ENTER("plugin_is_ready");
- mysql_mutex_lock(&LOCK_plugin);
- if ((plugin= plugin_find_internal(name, type)))
+ DBUG_ENTER("plugin_status");
+ if ((plugin= plugin_find_internal(global_plugins_state, name, type)))
{
rc= SHOW_OPTION_DISABLED;
if (plugin->state == PLUGIN_IS_READY)
rc= SHOW_OPTION_YES;
}
- mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(rc);
}
bool plugin_is_ready(const LEX_CSTRING *name, int type)
{
- bool rc= FALSE;
- if (plugin_status(name, type) == SHOW_OPTION_YES)
- rc= TRUE;
- return rc;
-}
-
-
-SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
-{
- LEX_CSTRING plugin_name= { name, len };
- return plugin_status(&plugin_name, type);
+ return plugin_status(name, type) == SHOW_OPTION_YES;
}
@@ -944,8 +942,6 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc,
st_plugin_int *pi= plugin_ref_to_int(rc);
DBUG_ENTER("intern_plugin_lock");
- mysql_mutex_assert_owner(&LOCK_plugin);
-
if (pi->state & state_mask)
{
plugin_ref plugin;
@@ -969,7 +965,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc,
*plugin= pi;
#endif
- pi->ref_count++;
+ my_atomic_add32(&pi->ref_count, 1);
DBUG_PRINT("lock",("thd: %p plugin: \"%s\" LOCK ref_count: %d",
current_thd, pi->name.str, pi->ref_count));
@@ -1019,10 +1015,8 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr)
DBUG_RETURN(ptr);
}
#endif
- mysql_mutex_lock(&LOCK_plugin);
- plugin_ref_to_int(ptr)->locks_total++;
+ my_atomic_add32(&plugin_ref_to_int(ptr)->locks_total, 1);
rc= intern_plugin_lock(lex, ptr);
- mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(rc);
}
@@ -1043,10 +1037,8 @@ plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type)
plugin_ref rc= NULL;
st_plugin_int *plugin;
DBUG_ENTER("plugin_lock_by_name");
- mysql_mutex_lock(&LOCK_plugin);
- if ((plugin= plugin_find_internal(name, type)))
+ if ((plugin= plugin_find_internal(global_plugins_state, name, type)))
rc= intern_plugin_lock(lex, plugin_int_to_ref(plugin));
- mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(rc);
}
@@ -1056,18 +1048,21 @@ 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++)
+ mysql_mutex_assert_owner(&LOCK_plugin);
+ for (i= 0; i < new_plugins_state->m_array.elements; i++)
{
- tmp= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
+ tmp= *dynamic_element(&new_plugins_state->m_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))
+ if (insert_dynamic(&new_plugins_state->m_array, (uchar*)&plugin))
DBUG_RETURN(0);
- tmp= *dynamic_element(&plugin_array, plugin_array.elements - 1,
+ tmp= *dynamic_element(&new_plugins_state->m_array,
+ new_plugins_state->m_array.elements - 1,
struct st_plugin_int **)=
(struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)plugin,
sizeof(struct st_plugin_int));
@@ -1088,7 +1083,9 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists,
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))
+ mysql_mutex_assert_owner(&LOCK_plugin);
+ if (name->str && plugin_find_internal(new_plugins_state, name,
+ MYSQL_ANY_PLUGIN))
{
if (if_not_exists)
MyFlags|= ME_NOTE;
@@ -1120,7 +1117,8 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists,
continue; // plugin name doesn't match
if (!name->str &&
- (maybe_dupe= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)))
+ (maybe_dupe= plugin_find_internal(new_plugins_state,
+ &tmp.name, MYSQL_ANY_PLUGIN)))
{
if (plugin->name != maybe_dupe->plugin->name)
{
@@ -1172,7 +1170,8 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists,
if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
goto err;
- if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
+ if (my_hash_insert(&new_plugins_state->m_hash[plugin->type],
+ (uchar*)tmp_plugin_ptr))
tmp_plugin_ptr->state= PLUGIN_IS_FREED;
init_alloc_root(&tmp_plugin_ptr->mem_root, "plugin", 4096, 4096, MYF(0));
@@ -1210,13 +1209,6 @@ static void plugin_variables_deinit(struct st_plugin_int *plugin)
static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
{
- /*
- we don't want to hold the LOCK_plugin mutex as it may cause
- deinitialization to deadlock if plugins have worker threads
- with plugin locks
- */
- mysql_mutex_assert_not_owner(&LOCK_plugin);
-
if (plugin->plugin->status_vars)
{
/*
@@ -1270,7 +1262,8 @@ static void plugin_del(struct st_plugin_int *plugin)
restore_ptr_backup(plugin->nbackups, plugin->ptr_backup);
if (plugin->plugin_dl)
{
- my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
+ my_hash_delete(&new_plugins_state->m_hash[plugin->plugin->type],
+ (uchar*)plugin);
plugin_dl_del(plugin->plugin_dl);
plugin->state= PLUGIN_IS_FREED;
free_root(&plugin->mem_root, MYF(0));
@@ -1280,24 +1273,25 @@ static void plugin_del(struct st_plugin_int *plugin)
DBUG_VOID_RETURN;
}
+
static void reap_plugins(void)
{
uint count;
struct st_plugin_int *plugin, **reap, **list;
- mysql_mutex_assert_owner(&LOCK_plugin);
-
if (!reap_needed)
return;
+ mysql_mutex_lock(&LOCK_plugin);
reap_needed= false;
- count= plugin_array.elements;
+ count= global_plugins_state->m_array.elements;
reap= (struct st_plugin_int **)my_alloca(sizeof(plugin)*(count+1));
*(reap++)= NULL;
for (uint i=0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
{
- HASH *hash= plugin_hash + plugin_type_initialization_order[i];
+ HASH *hash= global_plugins_state->m_hash +
+ plugin_type_initialization_order[i];
for (uint j= 0; j < hash->records; j++)
{
plugin= (struct st_plugin_int *) my_hash_element(hash, j);
@@ -1310,28 +1304,26 @@ static void reap_plugins(void)
}
}
- mysql_mutex_unlock(&LOCK_plugin);
list= reap;
while ((plugin= *(--list)))
plugin_deinitialize(plugin, true);
- mysql_mutex_lock(&LOCK_plugin);
while ((plugin= *(--reap)))
plugin_del(plugin);
+ mysql_mutex_unlock(&LOCK_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");
- mysql_mutex_assert_owner(&LOCK_plugin);
-
if (!plugin)
DBUG_VOID_RETURN;
@@ -1361,13 +1353,15 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
}
DBUG_ASSERT(pi->ref_count);
- pi->ref_count--;
+ my_atomic_add32(&pi->ref_count, -1);
DBUG_PRINT("lock",("thd: %p plugin: \"%s\" UNLOCK ref_count: %d",
current_thd, pi->name.str, pi->ref_count));
if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count)
+ {
reap_needed= true;
+ }
DBUG_VOID_RETURN;
}
@@ -1384,10 +1378,8 @@ void plugin_unlock(THD *thd, plugin_ref plugin)
if (!plugin_dlib(plugin))
DBUG_VOID_RETURN;
#endif
- mysql_mutex_lock(&LOCK_plugin);
intern_plugin_unlock(lex, plugin);
reap_plugins();
- mysql_mutex_unlock(&LOCK_plugin);
DBUG_VOID_RETURN;
}
@@ -1400,11 +1392,9 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count)
DBUG_VOID_RETURN;
DBUG_ASSERT(list);
- mysql_mutex_lock(&LOCK_plugin);
while (count--)
intern_plugin_unlock(lex, *list++);
reap_plugins();
- mysql_mutex_unlock(&LOCK_plugin);
DBUG_VOID_RETURN;
}
@@ -1419,8 +1409,6 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
uint state= plugin->state;
DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED);
- mysql_mutex_unlock(&LOCK_plugin);
-
mysql_prlock_wrlock(&LOCK_system_variables_hash);
if (test_plugin_options(tmp_root, plugin, argc, argv))
state= PLUGIN_IS_DISABLED;
@@ -1479,7 +1467,6 @@ err:
if (ret)
plugin_variables_deinit(plugin);
- mysql_mutex_lock(&LOCK_plugin);
plugin->state= state;
DBUG_RETURN(ret);
@@ -1542,6 +1529,38 @@ static void init_plugin_psi_keys(void)
}
#endif /* HAVE_PSI_INTERFACE */
+
+static bool copy_plugins_state(st_plugins_state *dest, st_plugins_state *src)
+{
+ uint i;
+
+ reset_dynamic(&dest->m_array);
+ for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
+ my_hash_reset(&dest->m_hash[i]);
+
+ for (i= 0; i < src->m_array.elements; i++)
+ {
+ struct st_plugin_int *ptr;
+ ptr= *dynamic_element(&src->m_array, i, struct st_plugin_int **);
+ if (insert_dynamic(&dest->m_array, (uchar *) &ptr) ||
+ (ptr->state != PLUGIN_IS_FREED &&
+ my_hash_insert(&dest->m_hash[ptr->plugin->type],
+ (uchar*) ptr)))
+ goto err;
+ }
+
+ return false;
+err:
+ return true;
+}
+
+
+static void set_new_global_plugins_state()
+{
+ swap_variables(st_plugins_state *, global_plugins_state, new_plugins_state);
+}
+
+
/*
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
@@ -1579,18 +1598,22 @@ int plugin_init(int *argc, char **argv, int flags)
Big enough to avoid many mallocs even in future
*/
if (my_init_dynamic_array(&plugin_dl_array,
- sizeof(struct st_plugin_dl *), 16, 16, MYF(0)) ||
- my_init_dynamic_array(&plugin_array,
- sizeof(struct st_plugin_int *), 80, 32, MYF(0)))
+ sizeof(struct st_plugin_dl *), 16, 16, MYF(0)))
goto err;
- for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
+ for (int ps=0; ps < 2; ps++)
{
- if (my_hash_init(&plugin_hash[i], system_charset_info, 32, 0, 0,
- get_plugin_hash_key, NULL, HASH_UNIQUE))
+ struct st_plugins_state *st= plugins_states + ps;
+ if (my_init_dynamic_array(&st->m_array,
+ sizeof(struct st_plugin_int *), 80, 32, MYF(0)))
goto err;
+ for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
+ {
+ if (my_hash_init(st->m_hash + i, system_charset_info, 32, 0, 0,
+ get_plugin_hash_key, NULL, HASH_UNIQUE))
+ goto err;
+ }
}
-
/* prepare debug_sync service */
DBUG_ASSERT(strcmp(list_of_services[1].name, "debug_sync_service") == 0);
list_of_services[1].service= *(void**)&debug_sync_C_callback_ptr;
@@ -1652,7 +1675,8 @@ int plugin_init(int *argc, char **argv, int flags)
First, we initialize only MyISAM - that should almost always succeed
(almost always, because plugins can be loaded outside of the server, too).
*/
- plugin_ptr= plugin_find_internal(&MyISAM, MYSQL_STORAGE_ENGINE_PLUGIN);
+ plugin_ptr= plugin_find_internal(new_plugins_state, &MyISAM,
+ MYSQL_STORAGE_ENGINE_PLUGIN);
DBUG_ASSERT(plugin_ptr || !mysql_mandatory_plugins[0]);
if (plugin_ptr)
{
@@ -1670,7 +1694,6 @@ int plugin_init(int *argc, char **argv, int flags)
DBUG_SLOW_ASSERT(plugin_ptr->ref_count == 1);
}
- mysql_mutex_unlock(&LOCK_plugin);
/* Register (not initialize!) all dynamic plugins */
if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING))
@@ -1703,15 +1726,15 @@ int plugin_init(int *argc, char **argv, int flags)
Now we initialize all remaining plugins
*/
- mysql_mutex_lock(&LOCK_plugin);
- reap= (st_plugin_int **) my_alloca((plugin_array.elements+1) * sizeof(void*));
+ reap= (st_plugin_int **) my_alloca((new_plugins_state->m_array.elements+1) *
+ sizeof(void*));
*(reap++)= NULL;
for(;;)
{
for (i=0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
{
- HASH *hash= plugin_hash + plugin_type_initialization_order[i];
+ HASH *hash= new_plugins_state->m_hash + plugin_type_initialization_order[i];
for (uint idx= 0; idx < hash->records; idx++)
{
plugin_ptr= (struct st_plugin_int *) my_hash_element(hash, idx);
@@ -1731,10 +1754,8 @@ int plugin_init(int *argc, char **argv, int flags)
if (flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE)
break;
- mysql_mutex_unlock(&LOCK_plugin);
plugin_load(&tmp_root);
flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE;
- mysql_mutex_lock(&LOCK_plugin);
}
/*
@@ -1742,11 +1763,9 @@ int plugin_init(int *argc, char **argv, int flags)
*/
while ((plugin_ptr= *(--reap)))
{
- mysql_mutex_unlock(&LOCK_plugin);
if (plugin_is_forced(plugin_ptr))
reaped_mandatory_plugin= TRUE;
plugin_deinitialize(plugin_ptr, true);
- mysql_mutex_lock(&LOCK_plugin);
plugin_del(plugin_ptr);
}
@@ -1756,6 +1775,7 @@ int plugin_init(int *argc, char **argv, int flags)
goto err;
free_root(&tmp_root, MYF(0));
+ set_new_global_plugins_state();
DBUG_RETURN(0);
@@ -1775,15 +1795,16 @@ static bool register_builtin(struct st_maria_plugin *plugin,
tmp->ref_count= 0;
tmp->plugin_dl= 0;
- if (insert_dynamic(&plugin_array, (uchar*)&tmp))
+ if (insert_dynamic(&new_plugins_state->m_array, (uchar*)&tmp))
DBUG_RETURN(1);
- *ptr= *dynamic_element(&plugin_array, plugin_array.elements - 1,
+ *ptr= *dynamic_element(&new_plugins_state->m_array,
+ new_plugins_state->m_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))
+ if (my_hash_insert(&new_plugins_state->m_hash[plugin->type],(uchar*) *ptr))
DBUG_RETURN(1);
DBUG_RETURN(0);
@@ -1801,8 +1822,6 @@ static void plugin_load(MEM_ROOT *tmp_root)
int error;
THD *new_thd= new THD(0);
bool result;
- unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
- { MYSQL_AUDIT_GENERAL_CLASSMASK };
DBUG_ENTER("plugin_load");
if (global_system_variables.log_warnings >= 9)
@@ -1815,7 +1834,9 @@ static void plugin_load(MEM_ROOT *tmp_root)
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_READ);
tables.open_strategy= TABLE_LIST::OPEN_NORMAL;
+ set_new_global_plugins_state();
result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT);
+ set_new_global_plugins_state();
table= tables.table;
if (result)
@@ -1852,51 +1873,30 @@ static void plugin_load(MEM_ROOT *tmp_root)
continue;
/*
- Pre-acquire audit plugins for events that may potentially occur
- during [UN]INSTALL PLUGIN.
-
- When audit event is triggered, audit subsystem acquires interested
- plugins by walking through plugin list. Evidently plugin list
- iterator protects plugin list by acquiring LOCK_plugin, see
- plugin_foreach_with_mask().
-
- On the other hand plugin_load is acquiring LOCK_plugin
- rather for a long time.
-
- When audit event is triggered during plugin_load plugin
- list iterator acquires the same lock (within the same thread)
- second time.
-
- This hack should be removed when LOCK_plugin is fixed so it
- protects only what it supposed to protect.
-
- See also mysql_install_plugin(), mysql_uninstall_plugin() and
- initialize_audit_plugin()
- */
- if (mysql_audit_general_enabled())
- mysql_audit_acquire_plugins(new_thd, event_class_mask);
-
- /*
there're no other threads running yet, so we don't need a mutex.
but plugin_add() before is designed to work in multi-threaded
environment, and it uses mysql_mutex_assert_owner(), so we lock
the mutex here to satisfy the assert
*/
- mysql_mutex_lock(&LOCK_plugin);
plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG));
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
- mysql_mutex_unlock(&LOCK_plugin);
}
if (unlikely(error > 0))
sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno,
table->file->table_type());
+ set_new_global_plugins_state();
end_read_record(&read_record_info);
table->mark_table_for_reopen();
close_mysql_tables(new_thd);
-end:
+err_ret:
new_thd->db= null_clex_str; // Avoid free on thd->db
delete new_thd;
+ set_new_global_plugins_state();
DBUG_VOID_RETURN;
+
+end:
+ set_new_global_plugins_state();
+ goto err_ret;
}
@@ -1935,7 +1935,6 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
}
dl= name;
- mysql_mutex_lock(&LOCK_plugin);
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
name.str= 0; // load everything
if (plugin_add(tmp_root, false, &name, &dl,
@@ -1945,12 +1944,10 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
else
{
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
- mysql_mutex_lock(&LOCK_plugin);
if (plugin_add(tmp_root, false, &name, &dl,
MYF(ME_ERROR_LOG)) != INSTALL_GOOD)
goto error;
}
- mysql_mutex_unlock(&LOCK_plugin);
name.length= dl.length= 0;
dl.str= NULL; name.str= p= buffer;
str= &name;
@@ -1972,7 +1969,6 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
}
DBUG_RETURN(FALSE);
error:
- mysql_mutex_unlock(&LOCK_plugin);
if (name.str)
sql_print_error("Couldn't load plugin '%s' from '%s'.",
name.str, dl.str);
@@ -1984,7 +1980,7 @@ error:
void plugin_shutdown(void)
{
- uint i, count= plugin_array.elements;
+ uint i, count;
struct st_plugin_int **plugins, *plugin;
struct st_plugin_dl **dl;
DBUG_ENTER("plugin_shutdown");
@@ -1997,8 +1993,6 @@ void plugin_shutdown(void)
opt_gtid_pos_auto_plugins= NULL;
}
- mysql_mutex_lock(&LOCK_plugin);
-
reap_needed= true;
/*
@@ -2008,12 +2002,13 @@ void plugin_shutdown(void)
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))
+ while (reap_needed && (count= global_plugins_state->m_array.elements))
{
reap_plugins();
for (i= 0; i < count; i++)
{
- plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
+ plugin= *dynamic_element(&global_plugins_state->m_array, i,
+ struct st_plugin_int **);
if (plugin->state == PLUGIN_IS_READY)
{
plugin->state= PLUGIN_IS_DELETED;
@@ -2032,18 +2027,20 @@ void plugin_shutdown(void)
plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1));
+ mysql_mutex_lock(&LOCK_plugin);
/*
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 **);
+ plugins[i]= *dynamic_element(&global_plugins_state->m_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;
}
- mysql_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
/*
We loop through all plugins and call deinit() if they have one.
*/
@@ -2058,13 +2055,8 @@ void plugin_shutdown(void)
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 mysql_mutex_assert_owner(), so we lock the mutex to satisfy it
- */
- mysql_mutex_lock(&LOCK_plugin);
+ mysql_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.
@@ -2078,6 +2070,7 @@ void plugin_shutdown(void)
plugins[i]->state & PLUGIN_IS_DISABLED)
plugin_del(plugins[i]);
}
+ mysql_mutex_unlock(&LOCK_plugin);
/*
Now we can deallocate all memory.
@@ -2085,7 +2078,6 @@ void plugin_shutdown(void)
cleanup_variables(&global_system_variables);
cleanup_variables(&max_system_variables);
- mysql_mutex_unlock(&LOCK_plugin);
initialized= 0;
mysql_mutex_destroy(&LOCK_plugin);
@@ -2095,9 +2087,13 @@ void plugin_shutdown(void)
/* Dispose of the memory */
- for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
- my_hash_free(&plugin_hash[i]);
- delete_dynamic(&plugin_array);
+ for (int ps=0; ps < 2; ps++)
+ {
+ struct st_plugins_state *st= plugins_states + ps;
+ for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
+ my_hash_free(&st->m_hash[i]);
+ delete_dynamic(&st->m_array);
+ }
count= plugin_dl_array.elements;
dl= (struct st_plugin_dl **)my_alloca(sizeof(void*) * count);
@@ -2125,7 +2121,8 @@ void plugin_shutdown(void)
static bool finalize_install(THD *thd, TABLE *table, const LEX_CSTRING *name,
int *argc, char **argv)
{
- struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN);
+ struct st_plugin_int *tmp= plugin_find_internal(new_plugins_state, name,
+ MYSQL_ANY_PLUGIN);
int error;
DBUG_ASSERT(tmp);
mysql_mutex_assert_owner(&LOCK_plugin); // because of tmp->state
@@ -2176,6 +2173,44 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_CSTRING *name,
return 0;
}
+
+/*
+ Request object for ending the new_plugins_state change.
+*/
+
+class Ping_all_threads_request : public Apc_target::Apc_call
+{
+public:
+ /* Overloaded virtual functions. */
+ void call_in_target_thread();
+};
+
+
+void Ping_all_threads_request::call_in_target_thread()
+{
+ /* Do nothing. We just make sure the thread got to this point. */
+}
+
+
+static my_bool fix_plugins_state_callback(THD *thd, THD *caller_thd)
+{
+ bool timed_out;
+ int timeout_sec= 30;
+
+ Ping_all_threads_request ping_req;
+
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ if (thd->get_command() == COM_SLEEP)
+ {
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
+ return FALSE;
+ }
+ thd->apc_target.make_apc_call(caller_thd, &ping_req,
+ timeout_sec, &timed_out);
+ return FALSE;
+}
+
+
bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
const LEX_CSTRING *dl_arg)
{
@@ -2185,8 +2220,6 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
enum install_status error;
int argc=orig_argc;
char **argv=orig_argv;
- unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
- { MYSQL_AUDIT_GENERAL_CLASSMASK };
DBUG_ENTER("mysql_install_plugin");
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE);
@@ -2205,35 +2238,22 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
DBUG_RETURN(TRUE);
}
- /*
- Pre-acquire audit plugins for events that may potentially occur
- during [UN]INSTALL PLUGIN.
-
- When audit event is triggered, audit subsystem acquires interested
- plugins by walking through plugin list. Evidently plugin list
- iterator protects plugin list by acquiring LOCK_plugin, see
- plugin_foreach_with_mask().
-
- On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
- rather for a long time.
-
- When audit event is triggered during [UN]INSTALL PLUGIN, plugin
- list iterator acquires the same lock (within the same thread)
- second time.
-
- This hack should be removed when LOCK_plugin is fixed so it
- protects only what it supposed to protect.
+ mysql_mutex_lock(&LOCK_plugin);
- See also mysql_uninstall_plugin() and initialize_audit_plugin()
- */
- if (mysql_audit_general_enabled())
- mysql_audit_acquire_plugins(thd, event_class_mask);
+ if (unlikely(copy_plugins_state(new_plugins_state, global_plugins_state)))
+ {
+ error= INSTALL_FAIL_NOT_OK;
+ mysql_mutex_unlock(&LOCK_plugin);
+ goto err;
+ }
- mysql_mutex_lock(&LOCK_plugin);
error= plugin_add(thd->mem_root, thd->lex->create_info.if_not_exists(),
name, &dl, MYF(0));
if (unlikely(error != INSTALL_GOOD))
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
goto err;
+ }
if (name->str)
error= finalize_install(thd, table, name, &argc, argv)
@@ -2252,12 +2272,19 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
if (unlikely(error != INSTALL_GOOD))
{
+ mysql_mutex_unlock(&LOCK_plugin);
reap_needed= true;
reap_plugins();
+ goto err;
}
-err:
global_plugin_version++;
+ set_new_global_plugins_state();
+ thd->apc_target.disable();
mysql_mutex_unlock(&LOCK_plugin);
+ server_threads.iterate(fix_plugins_state_callback, thd);
+ thd->apc_target.enable();
+
+err:
if (argv)
free_defaults(argv);
DBUG_RETURN(error == INSTALL_FAIL_NOT_OK);
@@ -2273,7 +2300,8 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name)
struct st_plugin_int *plugin;
mysql_mutex_assert_owner(&LOCK_plugin);
- if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) ||
+ if (!(plugin= plugin_find_internal(global_plugins_state,
+ name, MYSQL_ANY_PLUGIN)) ||
plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING))
{
// maybe plugin is present in mysql.plugin; postpone the error
@@ -2341,8 +2369,6 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
TABLE_LIST tables;
LEX_CSTRING dl= *dl_arg;
bool error= false;
- unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
- { MYSQL_AUDIT_GENERAL_CLASSMASK };
DBUG_ENTER("mysql_uninstall_plugin");
tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE);
@@ -2366,30 +2392,6 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
DBUG_RETURN(TRUE);
}
- /*
- Pre-acquire audit plugins for events that may potentially occur
- during [UN]INSTALL PLUGIN.
-
- When audit event is triggered, audit subsystem acquires interested
- plugins by walking through plugin list. Evidently plugin list
- iterator protects plugin list by acquiring LOCK_plugin, see
- plugin_foreach_with_mask().
-
- On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
- rather for a long time.
-
- When audit event is triggered during [UN]INSTALL PLUGIN, plugin
- list iterator acquires the same lock (within the same thread)
- second time.
-
- This hack should be removed when LOCK_plugin is fixed so it
- protects only what it supposed to protect.
-
- See also mysql_install_plugin() and initialize_audit_plugin()
- */
- if (mysql_audit_general_enabled())
- mysql_audit_acquire_plugins(thd, event_class_mask);
-
mysql_mutex_lock(&LOCK_plugin);
if (name->str)
@@ -2414,10 +2416,12 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name,
error|= !MyFlags;
}
}
- reap_plugins();
-
global_plugin_version++;
+ thd->apc_target.disable();
mysql_mutex_unlock(&LOCK_plugin);
+ server_threads.iterate(fix_plugins_state_callback, thd);
+ reap_plugins();
+ thd->apc_target.enable();
DBUG_RETURN(error);
#ifdef WITH_WSREP
wsrep_error_label:
@@ -2438,17 +2442,17 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
if (!initialized)
DBUG_RETURN(FALSE);
- mysql_mutex_lock(&LOCK_plugin);
/*
Do the alloca out here in case we do have a working alloca:
leaving the nested stack frame invalidates alloca allocation.
*/
if (type == MYSQL_ANY_PLUGIN)
{
- plugins= (plugin_ref*) my_alloca(plugin_array.elements * sizeof(plugin_ref));
- for (idx= 0; idx < plugin_array.elements; idx++)
+ plugins= (plugin_ref*) my_alloca(global_plugins_state->m_array.elements *
+ sizeof(plugin_ref));
+ for (idx= 0; idx < global_plugins_state->m_array.elements; idx++)
{
- plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
+ plugin= *dynamic_element(&global_plugins_state->m_array, idx, struct st_plugin_int **);
if ((plugins[total]= intern_plugin_lock(0, plugin_int_to_ref(plugin),
state_mask)))
total++;
@@ -2456,7 +2460,7 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
}
else
{
- HASH *hash= plugin_hash + type;
+ HASH *hash= global_plugins_state->m_hash + type;
plugins= (plugin_ref*) my_alloca(hash->records * sizeof(plugin_ref));
for (idx= 0; idx < hash->records; idx++)
{
@@ -2466,7 +2470,6 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
total++;
}
}
- mysql_mutex_unlock(&LOCK_plugin);
for (idx= 0; idx < total; idx++)
{
@@ -2485,6 +2488,7 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl,
st_maria_plugin *plug,
plugin_foreach_func *func, void *arg)
{
+ mysql_mutex_assert_owner(&LOCK_plugin);
for (; plug->name; plug++)
{
st_plugin_int tmp, *plugin;
@@ -2494,8 +2498,8 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl,
tmp.plugin= plug;
tmp.plugin_dl= plugin_dl;
- mysql_mutex_lock(&LOCK_plugin);
- if ((plugin= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)) &&
+ if ((plugin= plugin_find_internal(global_plugins_state, &tmp.name,
+ MYSQL_ANY_PLUGIN)) &&
plugin->plugin == plug)
{
@@ -2507,7 +2511,6 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl,
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))
@@ -2521,21 +2524,17 @@ bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl,
{
bool err= 0;
+ mysql_mutex_lock(&LOCK_plugin);
if (dl)
{
- mysql_mutex_lock(&LOCK_plugin);
st_plugin_dl *plugin_dl= plugin_dl_add(dl, MYF(0));
- 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
{
@@ -2545,6 +2544,7 @@ bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl,
for (builtins= mysql_optional_plugins; !err && *builtins; builtins++)
err= plugin_dl_foreach_internal(thd, 0, *builtins, func, arg);
}
+ mysql_mutex_unlock(&LOCK_plugin);
return err;
}
@@ -3198,7 +3198,6 @@ void plugin_thdvar_init(THD *thd)
thd->variables.dynamic_variables_size= 0;
thd->variables.dynamic_variables_ptr= 0;
- mysql_mutex_lock(&LOCK_plugin);
thd->variables.table_plugin=
intern_plugin_lock(NULL, global_system_variables.table_plugin);
if (global_system_variables.tmp_table_plugin)
@@ -3210,7 +3209,6 @@ void plugin_thdvar_init(THD *thd)
intern_plugin_unlock(NULL, old_table_plugin);
intern_plugin_unlock(NULL, old_tmp_table_plugin);
intern_plugin_unlock(NULL, old_enforced_table_plugin);
- mysql_mutex_unlock(&LOCK_plugin);
#ifndef EMBEDDED_LIBRARY
thd->session_tracker.sysvars.init(thd);
@@ -3284,8 +3282,6 @@ void plugin_thdvar_cleanup(THD *thd)
thd->session_tracker.sysvars.deinit(thd);
#endif
- mysql_mutex_lock(&LOCK_plugin);
-
unlock_variables(thd, &thd->variables);
cleanup_variables(&thd->variables);
@@ -3298,8 +3294,6 @@ void plugin_thdvar_cleanup(THD *thd)
}
reap_plugins();
- mysql_mutex_unlock(&LOCK_plugin);
-
reset_dynamic(&thd->lex->plugins);
DBUG_VOID_RETURN;
@@ -4245,9 +4239,10 @@ void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root)
if (!initialized)
return;
- for (uint idx= 0; idx < plugin_array.elements; idx++)
+ for (uint idx= 0; idx < global_plugins_state->m_array.elements; idx++)
{
- p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
+ p= *dynamic_element(&global_plugins_state->m_array, idx,
+ struct st_plugin_int **);
if (!(opt= construct_help_options(mem_root, p)))
continue;
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index c9c75d07a6e..3258f14a0ab 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -113,8 +113,8 @@ struct st_plugin_int
st_ptr_backup *ptr_backup;
uint nbackups;
uint state;
- uint ref_count; /* number of threads using the plugin */
- uint locks_total; /* how many times the plugin was locked */
+ volatile int32 ref_count; /* number of threads using the plugin */
+ volatile int32 locks_total; /* how many times the plugin was locked */
void *data; /* plugin type specific, e.g. handlerton */
MEM_ROOT mem_root; /* memory for dynamic plugin structures */
sys_var *system_vars; /* server variables for this plugin */
@@ -182,7 +182,6 @@ extern void plugin_thdvar_init(THD *thd);
extern void plugin_thdvar_cleanup(THD *thd);
sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *var);
void plugin_opt_set_limits(struct my_option *, const struct st_mysql_sys_var *);
-extern SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type);
extern bool check_valid_path(const char *path, size_t length);
extern void plugin_mutex_init();
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index d0bb0c816ec..cfcfb4f2b6b 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -26925,7 +26925,7 @@ int JOIN::save_explain_data_intern(Explain_query *output,
}
if (select_lex->is_top_level_node())
- output->query_plan_ready();
+ output->query_plan_set_ready();
DBUG_RETURN(0);
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index ba895384c1a..dc7cf1b1e24 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -3010,6 +3010,12 @@ void Show_explain_request::call_in_target_thread()
Query_arena backup_arena;
bool printed_anything= FALSE;
+ if (target_thd->lex == NULL || target_thd->lex->explain == NULL ||
+ !target_thd->lex->explain->query_plan_is_ready())
+ {
+ failed_to_produce= TRUE;
+ return;
+ }
/*
Change the arena because JOIN::print_explain and co. are going to allocate
items. Let them allocate them on our arena.
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 763fd8ec45b..b5f60681406 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -344,7 +344,7 @@ int table_value_constr::save_explain_data_intern(THD *thd,
output->add_node(explain);
if (select_lex->is_top_level_node())
- output->query_plan_ready();
+ output->query_plan_set_ready();
DBUG_RETURN(0);
}