diff options
-rw-r--r-- | mysql-test/r/udf_debug_sync.result | 20 | ||||
-rw-r--r-- | mysql-test/t/udf_debug_sync.test | 40 | ||||
-rw-r--r-- | sql/sql_udf.cc | 18 |
3 files changed, 73 insertions, 5 deletions
diff --git a/mysql-test/r/udf_debug_sync.result b/mysql-test/r/udf_debug_sync.result new file mode 100644 index 00000000000..2db75f9aa76 --- /dev/null +++ b/mysql-test/r/udf_debug_sync.result @@ -0,0 +1,20 @@ +CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "UDF_EXAMPLE_LIB"; +CREATE VIEW v1 AS SELECT myfunc_int(1); +SET debug_sync='mysql_create_function_after_lock SIGNAL locked WAIT_FOR go'; +CREATE FUNCTION myfunc_double RETURNS REAL SONAME "UDF_EXAMPLE_LIB"; +SET debug_sync='now WAIT_FOR locked'; +SET debug_sync='find_udf_before_lock SIGNAL go'; +SELECT * FROM v1; +myfunc_int(1) +1 +FLUSH TABLES; +SET debug_sync='mysql_drop_function_after_lock SIGNAL locked WAIT_FOR go'; +DROP FUNCTION myfunc_double; +SET debug_sync='now WAIT_FOR locked'; +SET debug_sync='find_udf_before_lock SIGNAL go'; +SELECT * FROM v1; +myfunc_int(1) +1 +SET debug_sync='RESET'; +DROP VIEW v1; +DROP FUNCTION myfunc_int; diff --git a/mysql-test/t/udf_debug_sync.test b/mysql-test/t/udf_debug_sync.test new file mode 100644 index 00000000000..593500c1e18 --- /dev/null +++ b/mysql-test/t/udf_debug_sync.test @@ -0,0 +1,40 @@ +--source include/have_debug_sync.inc +--source include/have_udf.inc + +# +# MDEV-5616 - Deadlock between CREATE/DROP FUNCTION and SELECT from view +# +--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB +eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO"; +CREATE VIEW v1 AS SELECT myfunc_int(1); +connect(con1, localhost, root,,); + +connection con1; +SET debug_sync='mysql_create_function_after_lock SIGNAL locked WAIT_FOR go'; +--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB +send_eval CREATE FUNCTION myfunc_double RETURNS REAL SONAME "$UDF_EXAMPLE_SO"; + +connection default; +SET debug_sync='now WAIT_FOR locked'; +SET debug_sync='find_udf_before_lock SIGNAL go'; +SELECT * FROM v1; +FLUSH TABLES; + +connection con1; +reap; +SET debug_sync='mysql_drop_function_after_lock SIGNAL locked WAIT_FOR go'; +send DROP FUNCTION myfunc_double; + +connection default; +SET debug_sync='now WAIT_FOR locked'; +SET debug_sync='find_udf_before_lock SIGNAL go'; +SELECT * FROM v1; + +connection con1; +reap; +disconnect con1; + +connection default; +SET debug_sync='RESET'; +DROP VIEW v1; +DROP FUNCTION myfunc_int; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 169e0d9e418..e5fac48a750 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -352,6 +352,7 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) if (!initialized) DBUG_RETURN(NULL); + DEBUG_SYNC(current_thd, "find_udf_before_lock"); /* TODO: This should be changed to reader locks someday! */ if (mark_used) mysql_rwlock_wrlock(&THR_LOCK_udf); /* Called during fix_fields */ @@ -466,7 +467,12 @@ int mysql_create_function(THD *thd,udf_func *udf) if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) thd->clear_current_stmt_binlog_format_row(); + tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), + "func", TL_WRITE); + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + mysql_rwlock_wrlock(&THR_LOCK_udf); + DEBUG_SYNC(current_thd, "mysql_create_function_after_lock"); if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length))) { my_error(ER_UDF_EXISTS, MYF(0), udf->name.str); @@ -510,9 +516,8 @@ int mysql_create_function(THD *thd,udf_func *udf) /* create entry in mysql.func table */ - tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); /* Allow creation of functions even if we can't open func table */ - if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) + if (!table) goto err; table->use_all_columns(); restore_record(table, s->default_values); // Default values for fields @@ -584,7 +589,12 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) thd->clear_current_stmt_binlog_format_row(); + tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"), + "func", TL_WRITE); + table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT); + mysql_rwlock_wrlock(&THR_LOCK_udf); + DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock"); if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str, (uint) udf_name->length))) { @@ -601,9 +611,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) if (udf->dlhandle && !find_udf_dl(udf->dl)) dlclose(udf->dlhandle); - tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE); - - if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) + if (!table) goto err; table->use_all_columns(); table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); |