List:Commits« Previous MessageNext Message »
From:Jon Olav Hauglid Date:January 10 2011 3:47pm
Subject:bzr commit into mysql-trunk branch (jon.hauglid:3479) Bug#53322
View as plain text  
#At file:///export/home/x/mysql-trunk-bug53322/ based on revid:matthias.leich@stripped

 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
=== 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 15:47:56 +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 15:47:56 +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 15:47:56 +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 */


Attachment: [text/bzr-bundle] bzr/jon.hauglid@oracle.com-20110110154756-mhdfcenzmjhc0g1n.bundle
Thread
bzr commit into mysql-trunk branch (jon.hauglid:3479) Bug#53322Jon Olav Hauglid10 Jan