List:Commits« Previous MessageNext Message »
From:Konstantin Osipov Date:October 30 2009 1:27pm
Subject:bzr commit into mysql-5.0 branch (kostja:2829) Bug#41756
View as plain text  
#At file:///opt/local/work/5.0-41756/ based on revid:sergey.glukhov@stripped

 2829 Konstantin Osipov	2009-10-30
      A preview of the fix for 
      Bug#41756 "Strange error messages about locks from InnoDB".
      
      Don't try to unlock rows unless certain that a) they were locked
      b) they are not used.
      An extended fix that counts locks.

    added:
      mysql-test/r/41756.result
      mysql-test/t/41756-master.opt
      mysql-test/t/41756.test
    modified:
      sql/item_subselect.cc
      sql/records.cc
      sql/sql_select.cc
      sql/sql_select.h
      sql/structs.h
=== added file 'mysql-test/r/41756.result'
--- a/mysql-test/r/41756.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/41756.result	2009-10-30 13:27:29 +0000
@@ -0,0 +1,20 @@
+#
+# Bug#41756 Strange error messages about locks from InnoDB
+#
+drop table if exists t1, t2;
+create table t1 (a int, b int primary key) engine=innodb;
+insert into t1 values (1,1), (null,2), (1,3), (1,4);
+create table t2 (a int, b int) engine=innodb;
+insert into t2 values (1,2), (1,2);
+select 1 from t1 natural join (select * from t2) as d for update;
+1
+commit;
+begin;
+select 1 from t1 natural join (select * from t2) as d for update;
+1
+#
+# Switching to connection con1
+delete from t1;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+commit;
+drop table t1, t2;

=== added file 'mysql-test/t/41756-master.opt'
--- a/mysql-test/t/41756-master.opt	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/41756-master.opt	2009-10-30 13:27:29 +0000
@@ -0,0 +1,2 @@
+--innodb-locks-unsafe-for-binlog
+--innodb-lock-wait-timeout=1

=== added file 'mysql-test/t/41756.test'
--- a/mysql-test/t/41756.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/41756.test	2009-10-30 13:27:29 +0000
@@ -0,0 +1,29 @@
+--source include/have_innodb.inc
+
+--echo #
+--echo # Bug#41756 Strange error messages about locks from InnoDB
+--echo #
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+
+create table t1 (a int, b int primary key) engine=innodb;
+insert into t1 values (1,1), (null,2), (1,3), (1,4);
+create table t2 (a int, b int) engine=innodb;
+insert into t2 values (1,2), (1,2);
+select 1 from t1 natural join (select * from t2) as d for update;
+commit;
+begin;
+select 1 from t1 natural join (select * from t2) as d for update;
+connect (con1,localhost,root,,);
+--echo #
+--echo # Switching to connection con1
+connection con1;
+--error ER_LOCK_WAIT_TIMEOUT
+delete from t1;
+disconnect con1;
+connection default;
+commit;
+
+drop table t1, t2;
+

=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc	2009-07-16 15:43:46 +0000
+++ b/sql/item_subselect.cc	2009-10-30 13:27:29 +0000
@@ -1844,6 +1844,7 @@ int subselect_single_select_engine::exec
     item->reset_value_registration();
     JOIN_TAB *changed_tabs[MAX_TABLES];
     JOIN_TAB **last_changed_tab= changed_tabs;
+    void rr_unlock_row(st_join_table *tab);
     if (item->have_guarded_conds())
     {
       /*
@@ -1869,6 +1870,7 @@ int subselect_single_select_engine::exec
               tab->read_record.record= tab->table->record[0];
               tab->read_record.thd= join->thd;
               tab->read_record.ref_length= tab->table->file->ref_length;
+              tab->read_record.unlock_row= rr_unlock_row;
               *(last_changed_tab++)= tab;
               break;
             }

=== modified file 'sql/records.cc'
--- a/sql/records.cc	2009-10-20 04:42:10 +0000
+++ b/sql/records.cc	2009-10-30 13:27:29 +0000
@@ -29,6 +29,7 @@ static int init_rr_cache(THD *thd, READ_
 static int rr_cmp(uchar *a,uchar *b);
 static int rr_index_first(READ_RECORD *info);
 static int rr_index(READ_RECORD *info);
+void rr_unlock_row(st_join_table *tab);
 
 
 /*
@@ -61,6 +62,7 @@ void init_read_record_idx(READ_RECORD *i
   info->file=  table->file;
   info->record= table->record[0];
   info->print_error= print_error;
+  info->unlock_row= rr_unlock_row;
 
   table->status=0;			/* And it's always found */
   if (!table->file->inited)
@@ -135,6 +137,7 @@ void init_read_record(READ_RECORD *info,
   }
   info->select=select;
   info->print_error=print_error;
+  info->unlock_row= rr_unlock_row;
   info->ignore_not_found_rows= 0;
   table->status=0;			/* And it's always found */
 

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-10-21 09:04:08 +0000
+++ b/sql/sql_select.cc	2009-10-30 13:27:29 +0000
@@ -5357,7 +5357,9 @@ static bool create_ref_for_key(JOIN *joi
   }
   j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length);
   j->ref.key_err=1;
+  j->ref.has_record= FALSE;
   j->ref.null_rejecting= 0;
+  j->ref.use_count= 0;
   keyuse=org_keyuse;
 
   store_key **ref_key= j->ref.key_copy;
@@ -6157,6 +6159,23 @@ make_join_select(JOIN *join,SQL_SELECT *
   DBUG_RETURN(0);
 }
 
+static
+void
+join_read_key_unlock_row(st_join_table *tab)
+{
+  DBUG_ASSERT(tab->ref.use_count);
+  if (tab->ref.use_count)
+    tab->ref.use_count--;
+}
+
+
+void rr_unlock_row(st_join_table *tab)
+{
+  READ_RECORD *info= &tab->read_record;
+  info->file->unlock_row();
+}
+
+
 static void
 make_join_readinfo(JOIN *join, ulonglong options)
 {
@@ -6172,6 +6191,7 @@ make_join_readinfo(JOIN *join, ulonglong
     TABLE *table=tab->table;
     tab->read_record.table= table;
     tab->read_record.file=table->file;
+    tab->read_record.unlock_row= rr_unlock_row;
     tab->next_select=sub_select;		/* normal select */
 
     /*
@@ -6215,6 +6235,7 @@ make_join_readinfo(JOIN *join, ulonglong
       delete tab->quick;
       tab->quick=0;
       tab->read_first_record= join_read_key;
+      tab->read_record.unlock_row= join_read_key_unlock_row;
       tab->read_record.read_record= join_no_more_records;
       if (table->used_keys.is_set(tab->ref.key) &&
 	  !table->no_keyread)
@@ -10902,7 +10923,7 @@ evaluate_join_record(JOIN *join, JOIN_TA
         return NESTED_LOOP_NO_MORE_ROWS;
     }
     else
-      join_tab->read_record.file->unlock_row();
+      join_tab->read_record.unlock_row(join_tab);
   }
   else
   {
@@ -10912,7 +10933,7 @@ evaluate_join_record(JOIN *join, JOIN_TA
     */
     join->examined_rows++;
     join->thd->row_count++;
-    join_tab->read_record.file->unlock_row();
+    join_tab->read_record.unlock_row(join_tab);
   }
   return NESTED_LOOP_OK;
 }
@@ -11265,12 +11286,23 @@ join_read_key(JOIN_TAB *tab)
       table->status=STATUS_NOT_FOUND;
       return -1;
     }
+    /*
+      Moving away from the current record. Unlock the row
+      in the handler if it was not used.
+    */
+    if (tab->ref.has_record && tab->ref.use_count == 0)
+      tab->read_record.file->unlock_row();
     error=table->file->index_read(table->record[0],
 				  tab->ref.key_buff,
 				  tab->ref.key_length,HA_READ_KEY_EXACT);
     if (error && error != HA_ERR_KEY_NOT_FOUND)
       return report_error(table, error);
+
+    tab->ref.use_count= 1;
+    tab->ref.has_record= TRUE;
   }
+  else if (table->status == 0)
+    tab->ref.use_count++;
   table->null_row=0;
   return table->status ? -1 : 0;
 }

=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h	2009-07-16 12:19:22 +0000
+++ b/sql/sql_select.h	2009-10-30 13:27:29 +0000
@@ -53,6 +53,8 @@ class store_key;
 typedef struct st_table_ref
 {
   bool		key_err;
+  /** True if something was read into buffer in join_read_key.  */
+  bool          has_record;
   uint          key_parts;                // num of ...
   uint          key_length;               // length of key_buff
   int           key;                      // key no
@@ -78,8 +80,13 @@ typedef struct st_table_ref
   */
   key_part_map  null_rejecting;
   table_map	depend_map;		  // Table depends on these tables.
-  byte          *null_ref_key;		  // null byte position in the key_buf.
-  					  // used for REF_OR_NULL optimization.
+  /* NULL byte position in the key_buf. Used for REF_OR_NULL optimization. */
+  byte          *null_ref_key;
+  /*
+    The number of times the record associated with this key was used
+    in the join.
+  */
+  ha_rows       use_count;
 } TABLE_REF;
 
 /*

=== modified file 'sql/structs.h'
--- a/sql/structs.h	2009-01-21 18:45:23 +0000
+++ b/sql/structs.h	2009-10-30 13:27:29 +0000
@@ -121,12 +121,16 @@ struct st_read_record;				/* For referen
 class SQL_SELECT;
 class THD;
 class handler;
+struct st_join_table;
+
+typedef void (*Unlock_row_func)(st_join_table *);
 
 typedef struct st_read_record {			/* Parameter to read_record */
   struct st_table *table;			/* Head-form */
   handler *file;
   struct st_table **forms;			/* head and ref forms */
   int (*read_record)(struct st_read_record *);
+  Unlock_row_func unlock_row;
   THD *thd;
   SQL_SELECT *select;
   uint cache_records;


Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20091030132729-hwwti0293rhotwq3.bundle
Thread
bzr commit into mysql-5.0 branch (kostja:2829) Bug#41756Konstantin Osipov30 Oct