3479 Jon Olav Hauglid 2011-01-10
Bug #53322 deadlock with FLUSH TABLES WITH READ LOCK and DROP FUNCTION
This deadlock could occur between two connections if one connection
first locked the mysql.func table (using either FLUSH TABLES WITH
READ LOCK or LOCK TABLE mysql.func WRITE). If the second connection
then tried to either CREATE or DROP an UDF function, a deadlock would
occur when the first connection tried to use an UDF function.
The reason for the deadlock was the way the THR_LOCK_udf rwlock was
used in the UDF handling code. For CREATE or DROP FUNCTION (UDF),
THR_LOCK_udf was write locked before mysql.func was locked and opened.
This meant that another connection first locking mysql.func and later
using an UDF function (and thus locking THR_LOCK_udf), could cause
a deadlock.
This patch fixes the problem by changing the CREATE and DROP FUNCTION
(UDF) implementation to open mysql.func before locking THR_LOCK_udf.
Test case added to udf.test.
modified:
mysql-test/r/udf.result
mysql-test/t/udf.test
sql/sql_udf.cc
3478 Matthias Leich 2011-01-10 [merge]
Upmerge of some post bug 58414 fix
modified:
mysql-test/collections/default.experimental
=== modified file 'mysql-test/r/udf.result'
--- a/mysql-test/r/udf.result 2010-03-24 15:03:44 +0000
+++ b/mysql-test/r/udf.result 2011-01-10 16:27:45 +0000
@@ -468,3 +468,34 @@ DROP FUNCTION myfunc_double;
DROP TABLE t1;
#
End of 5.1 tests.
+#
+# Bug#53322 deadlock with FLUSH TABLES WITH READ LOCK and DROP FUNCTION
+#
+CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
+CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
+# Connection con1
+FLUSH TABLES WITH READ LOCK;
+# Connection default
+# Sending:
+DROP FUNCTION metaphon;
+# Connection con1
+# Wait until DROP FUNCTION is blocked by GRL
+SELECT metaphon("foo");
+metaphon("foo")
+F
+UNLOCK TABLES;
+# Connection default
+# Reaping: DROP FUNCTION metaphon
+# Connection con1
+FLUSH TABLES WITH READ LOCK;
+# Connection default
+# Sending:
+CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB";
+# Connection con1
+# Wait until CREATE FUNCTION is blocked by GRL
+SELECT reverse_lookup("127.0.0.1");
+UNLOCK TABLES;
+# Connection default
+# Reaping: CREATE FUNCTION metaphon ...
+DROP FUNCTION metaphon;
+DROP FUNCTION reverse_lookup;
=== modified file 'mysql-test/t/udf.test'
--- a/mysql-test/t/udf.test 2010-03-24 15:03:44 +0000
+++ b/mysql-test/t/udf.test 2011-01-10 16:27:45 +0000
@@ -1,4 +1,5 @@
--source include/have_udf.inc
+--source include/not_embedded.inc
#
# To run this tests the "sql/udf_example.c" need to be compiled into
# udf_example.so and LD_LIBRARY_PATH should be setup to point out where
@@ -535,3 +536,70 @@ DROP TABLE t1;
--echo #
--echo End of 5.1 tests.
+
+--echo #
+--echo # Bug#53322 deadlock with FLUSH TABLES WITH READ LOCK and DROP FUNCTION
+--echo #
+
+--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB
+eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_LIB";
+--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB
+eval CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "$UDF_EXAMPLE_LIB";
+
+--echo # Connection con1
+connect(con1, localhost, root);
+FLUSH TABLES WITH READ LOCK;
+
+--echo # Connection default
+connection default;
+--echo # Sending:
+--send DROP FUNCTION metaphon
+
+--echo # Connection con1
+connection con1;
+--echo # Wait until DROP FUNCTION is blocked by GRL
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM information_schema.processlist
+ WHERE state = "Waiting for global read lock" AND
+ info = "DROP FUNCTION metaphon";
+--source include/wait_condition.inc
+SELECT metaphon("foo");
+UNLOCK TABLES;
+
+--echo # Connection default
+connection default;
+--echo # Reaping: DROP FUNCTION metaphon
+--reap
+
+--echo # Connection con1
+connection con1;
+FLUSH TABLES WITH READ LOCK;
+
+--echo # Connection default
+connection default;
+--echo # Sending:
+--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB
+--send
+eval CREATE FUNCTION metaphon RETURNS STRING SONAME "$UDF_EXAMPLE_LIB";
+
+--echo # Connection con1
+connection con1;
+--echo # Wait until CREATE FUNCTION is blocked by GRL
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM information_schema.processlist
+ WHERE state = "Waiting for global read lock";
+--source include/wait_condition.inc
+# Can return different results depending on platform
+--disable_result_log
+SELECT reverse_lookup("127.0.0.1");
+--enable_result_log
+UNLOCK TABLES;
+disconnect con1;
+--source include/wait_until_disconnected.inc
+
+--echo # Connection default
+connection default;
+--echo # Reaping: CREATE FUNCTION metaphon ...
+--reap
+DROP FUNCTION metaphon;
+DROP FUNCTION reverse_lookup;
=== modified file 'sql/sql_udf.cc'
--- a/sql/sql_udf.cc 2010-12-02 05:40:58 +0000
+++ b/sql/sql_udf.cc 2011-01-10 16:27:45 +0000
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
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
@@ -452,6 +452,10 @@ int mysql_create_function(THD *thd,udf_f
DBUG_RETURN(1);
}
+ tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
+ if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
+ DBUG_RETURN(1);
+
/*
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for CREATE FUNCTION command.
@@ -501,10 +505,6 @@ int mysql_create_function(THD *thd,udf_f
/* 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)))
- goto err;
table->use_all_columns();
restore_record(table, s->default_values); // Default values for fields
table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
@@ -557,6 +557,7 @@ int mysql_drop_function(THD *thd,const L
char *exact_name_str;
uint exact_name_len;
bool save_binlog_row_based;
+ int error= 1;
DBUG_ENTER("mysql_drop_function");
if (!initialized)
@@ -568,6 +569,10 @@ int mysql_drop_function(THD *thd,const L
DBUG_RETURN(1);
}
+ tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
+ if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
+ DBUG_RETURN(1);
+
/*
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for DROP FUNCTION command.
@@ -580,7 +585,8 @@ int mysql_drop_function(THD *thd,const L
(uint) udf_name->length)))
{
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
- goto err;
+ mysql_rwlock_unlock(&THR_LOCK_udf);
+ goto exit;
}
exact_name_str= udf->name.str;
exact_name_len= udf->name.length;
@@ -591,11 +597,8 @@ int mysql_drop_function(THD *thd,const L
*/
if (udf->dlhandle && !find_udf_dl(udf->dl))
dlclose(udf->dlhandle);
+ mysql_rwlock_unlock(&THR_LOCK_udf);
- tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
-
- if (!(table = open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
- goto err;
table->use_all_columns();
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
if (!table->file->ha_index_read_idx_map(table->record[0], 0,
@@ -603,36 +606,23 @@ int mysql_drop_function(THD *thd,const L
HA_WHOLE_KEY,
HA_READ_KEY_EXACT))
{
- int error;
- if ((error = table->file->ha_delete_row(table->record[0])))
- table->file->print_error(error, MYF(0));
+ int delete_err;
+ if ((delete_err = table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(delete_err, MYF(0));
}
- mysql_rwlock_unlock(&THR_LOCK_udf);
/*
Binlog the drop function. Keep the table open and locked
while binlogging, to avoid binlog inconsistency.
*/
- if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
- DBUG_RETURN(1);
- }
+ if (!write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
+ error= 0;
+exit:
/* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
thd->set_current_stmt_binlog_format_row();
- DBUG_RETURN(0);
-err:
- mysql_rwlock_unlock(&THR_LOCK_udf);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
- DBUG_RETURN(1);
+ DBUG_RETURN(error);
}
#endif /* HAVE_DLOPEN */
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (jon.hauglid:3478 to 3479) Bug#53322 | Jon Olav Hauglid | 10 Jan |