List:Commits« Previous MessageNext Message »
From:Jon Olav Hauglid Date:December 20 2010 12:59pm
Subject:bzr commit into mysql-trunk-bugfixing branch (jon.hauglid:3450) Bug#53322
View as plain text  
#At file:///export/home/x/mysql-trunk-bugfixing-bug53322/ based on revid:bar@stripped

 3450 Jon Olav Hauglid	2010-12-20
      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 FUNCTION (UDF)
      implementation to open mysql.func before locking THR_LOCK_udf. The
      DROP FUNCTION (UDF) implementation is changed so that THR_LOCK_udf
      is unlocked before opening mysql.func.
      
      Test case added to udf.test.

    modified:
      mysql-test/r/udf.result
      mysql-test/t/udf.test
      sql/sql_udf.cc
=== 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	2010-12-20 12:59:07 +0000
@@ -468,3 +468,33 @@ 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");
+ERROR 42000: FUNCTION test.metaphon does not exist
+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	2010-12-20 12:59:07 +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,71 @@ 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
+--error ER_SP_DOES_NOT_EXIST
+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	2010-12-20 12:59:07 +0000
@@ -459,6 +459,9 @@ int mysql_create_function(THD *thd,udf_f
   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
     thd->clear_current_stmt_binlog_format_row();
 
+  tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
+  table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
+
   mysql_rwlock_wrlock(&THR_LOCK_udf);
   if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
   {
@@ -501,9 +504,8 @@ 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)))
+  if (table == NULL)
     goto err;
   table->use_all_columns();
   restore_record(table, s->default_values);	// Default values for fields
@@ -557,6 +559,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)
@@ -580,7 +583,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 +595,12 @@ 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;
+    goto exit;
   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 +608,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 */


Attachment: [text/bzr-bundle] bzr/jon.hauglid@oracle.com-20101220125907-bwcvz39oxyfhq9i2.bundle
Thread
bzr commit into mysql-trunk-bugfixing branch (jon.hauglid:3450) Bug#53322Jon Olav Hauglid20 Dec
  • Re: bzr commit into mysql-trunk-bugfixing branch (jon.hauglid:3450)Bug#53322Davi Arnaut10 Jan