List:Commits« Previous MessageNext Message »
From:Georgi Kodinov Date:February 19 2009 3:30pm
Subject:bzr commit into mysql-5.0-bugteam branch (joro:2742) Bug#42419
View as plain text  
#At file:///home/kgeorge/mysql/work/B42419-5.0-bugteam/ based on revid:matthias.leich@stripped

 2742 Georgi Kodinov	2009-02-19
      Bug #42419: Server crash with "Pure virtual method called" on two concurrent
      connections
      The problem is that tables can enter open table cache for a thread without 
      being properly cleaned up. This can happen if make_join_statistics() fails 
      to read a const table because of e.g. a deadlock. It does set a member of 
      TABLE structure to a value it allocates, but doesn't clean-up this setting 
      on error nor does it set the rest of the members in JOIN to allow for 
      automatic cleanup.
      As a result when such an error occurs and the next statement depends re-uses 
      the table from the open tables cache it will get it with this 
      TABLE::reginfo.join_tab pointing to a memory area that's freed.
      Fixed by making sure make_join_statistics() cleans up TABLE::reginfo.join_tab 
      on error.
     @ mysql-test/r/innodb_mysql.result
        Bug #42419: test case
     @ mysql-test/t/innodb_mysql-master.opt
        Bug #42419: increase the timeout so it covers te conservative 
        sleep 3 in the test
     @ mysql-test/t/innodb_mysql.test
        Bug #42419: test case
     @ sql/sql_select.cc
        Bug #42419: clean up the members of TABLE on failure in 
                make_join_statisitcs()

    modified:
      mysql-test/r/innodb_mysql.result
      mysql-test/t/innodb_mysql-master.opt
      mysql-test/t/innodb_mysql.test
      sql/sql_select.cc
=== modified file 'mysql-test/r/innodb_mysql.result'
--- a/mysql-test/r/innodb_mysql.result	2008-11-27 14:54:23 +0000
+++ b/mysql-test/r/innodb_mysql.result	2009-02-19 15:30:03 +0000
@@ -1267,4 +1267,20 @@ CREATE INDEX i1 on t1 (a(3));
 SELECT * FROM t1 WHERE a = 'abcde';
 a
 DROP TABLE t1;
+CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT)
+ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+SET AUTOCOMMIT = 0;
+CREATE TEMPORARY TABLE t1_tmp (b INT);
+INSERT INTO t1_tmp SELECT b FROM t1 WHERE a = 3;
+INSERT INTO t1_tmp SELECT b FROM t1 WHERE a = 2;
+SET AUTOCOMMIT = 0;
+CREATE TEMPORARY TABLE t2_tmp ( a INT, new_a INT);
+INSERT INTO t2_tmp VALUES (1,51),(2,52),(3,53);
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 1;
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 2;
+INSERT INTO t1_tmp SELECT b FROM t1 WHERE a = 1;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 3;
+DROP TABLE t1;
 End of 5.0 tests

=== modified file 'mysql-test/t/innodb_mysql-master.opt'
--- a/mysql-test/t/innodb_mysql-master.opt	2006-12-19 23:57:51 +0000
+++ b/mysql-test/t/innodb_mysql-master.opt	2009-02-19 15:30:03 +0000
@@ -1 +1 @@
---innodb-lock-wait-timeout=2
+--innodb-lock-wait-timeout=3

=== modified file 'mysql-test/t/innodb_mysql.test'
--- a/mysql-test/t/innodb_mysql.test	2008-11-27 14:54:23 +0000
+++ b/mysql-test/t/innodb_mysql.test	2009-02-19 15:30:03 +0000
@@ -1025,4 +1025,55 @@ CREATE INDEX i1 on t1 (a(3)); 
 SELECT * FROM t1 WHERE a = 'abcde'; 
 DROP TABLE t1;
 
+#
+# Bug #42419: Server crash with "Pure virtual method called" on two
+# concurrent connections
+#
+
+connect (c1, localhost, root,,);
+connect (c2, localhost, root,,);
+
+CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT)
+ ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+
+connection c1;
+
+SET AUTOCOMMIT = 0;
+
+CREATE TEMPORARY TABLE t1_tmp (b INT);
+
+INSERT INTO t1_tmp SELECT b FROM t1 WHERE a = 3;
+INSERT INTO t1_tmp SELECT b FROM t1 WHERE a = 2;
+
+connection c2;
+
+SET AUTOCOMMIT = 0;
+
+CREATE TEMPORARY TABLE t2_tmp ( a INT, new_a INT);
+INSERT INTO t2_tmp VALUES (1,51),(2,52),(3,53);
+
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 1;
+
+--send
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 2;
+
+--sleep 3
+
+connection c1;
+
+--error ER_LOCK_DEADLOCK
+INSERT INTO t1_tmp SELECT b FROM t1 WHERE a = 1;
+
+connection c2;
+
+--reap
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 3;
+
+connection default;
+disconnect c1;
+disconnect c2;
+DROP TABLE t1;
+
 --echo End of 5.0 tests

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-02-05 09:30:39 +0000
+++ b/sql/sql_select.cc	2009-02-19 15:30:03 +0000
@@ -2373,11 +2373,12 @@ typedef struct st_sargable_param
 */
 
 static bool
-make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
+make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
 		     DYNAMIC_ARRAY *keyuse_array)
 {
   int error;
   TABLE *table;
+  TABLE_LIST *tables= tables_arg;
   uint i,table_count,const_count,key;
   table_map found_const_table_map, all_table_map, found_ref, refs;
   key_map const_ref, eq_part;
@@ -2415,10 +2416,10 @@ make_join_statistics(JOIN *join, TABLE_L
     table_vector[i]=s->table=table=tables->table;
     table->pos_in_table_list= tables;
     error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
-    if(error)
+    if (error)
     {
-        table->file->print_error(error, MYF(0));
-        DBUG_RETURN(1);
+      table->file->print_error(error, MYF(0));
+      goto error;
     }
     table->quick_keys.clear_all();
     table->reginfo.join_tab=s;
@@ -2503,7 +2504,7 @@ make_join_statistics(JOIN *join, TABLE_L
       {
         join->tables=0;			// Don't use join->table
         my_message(ER_WRONG_OUTER_JOIN, ER(ER_WRONG_OUTER_JOIN), MYF(0));
-        DBUG_RETURN(1);
+        goto error;
       }
       s->key_dependent= s->dependent;
     }
@@ -2513,7 +2514,7 @@ make_join_statistics(JOIN *join, TABLE_L
     if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables,
                             conds, join->cond_equal,
                             ~outer_join, join->select_lex, &sargables))
-      DBUG_RETURN(1);
+      goto error;
 
   /* Read tables with 0 or 1 rows (system tables) */
   join->const_table_map= 0;
@@ -2529,7 +2530,7 @@ make_join_statistics(JOIN *join, TABLE_L
     if ((tmp=join_read_const_table(s, p_pos)))
     {
       if (tmp > 0)
-	DBUG_RETURN(1);			// Fatal error
+	goto error;		// Fatal error
     }
     else
       found_const_table_map|= s->table->map;
@@ -2601,7 +2602,7 @@ make_join_statistics(JOIN *join, TABLE_L
 	  if ((tmp= join_read_const_table(s, join->positions+const_count-1)))
 	  {
 	    if (tmp > 0)
-	      DBUG_RETURN(1);			// Fatal error
+	      goto error;			// Fatal error
 	  }
 	  else
 	    found_const_table_map|= table->map;
@@ -2650,12 +2651,12 @@ make_join_statistics(JOIN *join, TABLE_L
 	        set_position(join,const_count++,s,start_keyuse);
 	        if (create_ref_for_key(join, s, start_keyuse,
 				       found_const_table_map))
-		  DBUG_RETURN(1);
+                  goto error;
 	        if ((tmp=join_read_const_table(s,
                                                join->positions+const_count-1)))
 	        {
 		  if (tmp > 0)
-		    DBUG_RETURN(1);			// Fatal error
+		    goto error;			// Fatal error
 	        }
 	        else
 		  found_const_table_map|= table->map;
@@ -2732,7 +2733,7 @@ make_join_statistics(JOIN *join, TABLE_L
 			  *s->on_expr_ref ? *s->on_expr_ref : conds,
 			  1, &error);
       if (!select)
-        DBUG_RETURN(1);
+        goto error;
       records= get_quick_record_count(join->thd, select, s->table,
 				      &s->const_keys, join->row_limit);
       s->quick=select->quick;
@@ -2778,7 +2779,7 @@ make_join_statistics(JOIN *join, TABLE_L
   {
     optimize_keyuse(join, keyuse_array);
     if (choose_plan(join, all_table_map & ~join->const_table_map))
-      DBUG_RETURN(TRUE);
+      goto error;
   }
   else
   {
@@ -2788,6 +2789,17 @@ make_join_statistics(JOIN *join, TABLE_L
   }
   /* Generate an execution plan from the found optimal join order. */
   DBUG_RETURN(join->thd->killed || get_best_combination(join));
+
+error:
+  /*
+    Need to clean up join_tab from TABLEs in case of error.
+    They won't get cleaned up by JOIN::cleanup() because JOIN::join_tab
+    may not be assigned yet by this function (which is building join_tab).
+    Dangling TABLE::reginfo.join_tab may cause part_of_refkey to choke. 
+  */
+  for (tables= tables_arg; tables; tables= tables->next_leaf)
+    tables->table->reginfo.join_tab= NULL;
+  DBUG_RETURN (1);
 }
 
 


Attachment: [text/bzr-bundle] bzr/joro@sun.com-20090219153003-f2j0ihginn4pqqdy.bundle
Thread
bzr commit into mysql-5.0-bugteam branch (joro:2742) Bug#42419Georgi Kodinov19 Feb