List:Commits« Previous MessageNext Message »
From:kevin.lewis Date:April 26 2011 7:07pm
Subject:bzr push into mysql-5.5-innodb branch (kevin.lewis:3370 to 3371) Bug#60309
Bug#12356829
View as plain text  
 3371 kevin.lewis@stripped	2011-04-26
      Bug#60309 -  Bug#12356829: MYSQL 5.5.9 FOR MAC OSX HAS BUG WITH FOREIGN KEY CONSTRAINTS
      
      The innoDB global variable srv_lower_case_table_names is set to the value of lower_case_table_names declared in mysqld.h server in ha_innodb.cc.  Since this variable can change at runtime, it is reset for each handler call to ::create, ::open, ::rename_table & ::delete_table.
      
      But it is possible for tables to be implicitly opened before an explicit handler call is made when an engine is first started or restarted.  I was able to reproduce that with the testcase in this patch on a version of InnoDB from 2 weeks ago.  It seemed like the change buffer entries for the secondary key was getting put into pages after the restart.  (But I am not sure, I did not write down the call stack while it was reproducing.)  In the current code, the implicit open, which is actually a call to dict_load_foreigns(), does not occur with this testcase.
      
      The change is to replace srv_lower_case_table_names by an interface function in innodb.cc that retrieves the server global variable when it is needed.

    modified:
      mysql-test/suite/innodb/r/innodb_bug60196.result
      mysql-test/suite/innodb/t/innodb_bug60196.test
      storage/innobase/dict/dict0dict.c
      storage/innobase/dict/dict0load.c
      storage/innobase/dict/dict0mem.c
      storage/innobase/handler/ha_innodb.cc
      storage/innobase/include/dict0mem.h
      storage/innobase/include/ha_prototypes.h
      storage/innobase/include/srv0srv.h
      storage/innobase/srv/srv0srv.c
 3370 Vasil Dimov	2011-04-21 [merge]
      Merge mysql-5.1-innodb -> mysql-5.5-innodb (empty)

=== modified file 'mysql-test/suite/innodb/r/innodb_bug60196.result'
--- a/mysql-test/suite/innodb/r/innodb_bug60196.result	revid:vasil.dimov@stripped
+++ b/mysql-test/suite/innodb/r/innodb_bug60196.result	revid:kevin.lewis@stripped
@@ -71,3 +71,47 @@ FK1_Key	FK2_Key
 DROP TABLE Bug_60196;
 DROP TABLE Bug_60196_FK1;
 DROP TABLE Bug_60196_FK2;
+CREATE TABLE Bug_60309_FK (
+ID INT PRIMARY KEY,
+ID2 INT,
+KEY K2(ID2)
+) ENGINE=InnoDB;
+CREATE TABLE Bug_60309 (
+ID INT PRIMARY KEY,
+FK_ID INT,
+KEY (FK_ID),
+CONSTRAINT FK FOREIGN KEY (FK_ID) REFERENCES Bug_60309_FK (ID)
+) ENGINE=InnoDB;
+INSERT INTO Bug_60309_FK (ID, ID2) VALUES (1, 1), (2, 2), (3, 3);
+INSERT INTO Bug_60309 VALUES (1, 1);
+INSERT INTO Bug_60309 VALUES (2, 99);
+ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`bug_60309`, CONSTRAINT `FK` FOREIGN KEY (`FK_ID`) REFERENCES `Bug_60309_FK` (`ID`))
+SELECT * FROM Bug_60309_FK;
+ID	ID2
+1	1
+2	2
+3	3
+SELECT * FROM Bug_60309;
+ID	FK_ID
+1	1
+# Stop server
+# Restart server.
+#
+# Try to insert more to the example table with foreign keys.
+# Bug60309 causes the foreign key file not to be found after
+# the resstart above.
+#
+SELECT * FROM Bug_60309;
+ID	FK_ID
+1	1
+INSERT INTO Bug_60309 VALUES (2, 2);
+INSERT INTO Bug_60309 VALUES (3, 3);
+SELECT * FROM Bug_60309;
+ID	FK_ID
+1	1
+2	2
+3	3
+
+# Clean up.
+DROP TABLE Bug_60309;
+DROP TABLE Bug_60309_FK;

=== modified file 'mysql-test/suite/innodb/t/innodb_bug60196.test'
--- a/mysql-test/suite/innodb/t/innodb_bug60196.test	revid:vasil.dimov@stripped
+++ b/mysql-test/suite/innodb/t/innodb_bug60196.test	revid:kevin.lewis@stripped
@@ -85,3 +85,73 @@ DROP TABLE Bug_60196;
 DROP TABLE Bug_60196_FK1;
 DROP TABLE Bug_60196_FK2;
 
+
+# Bug#60309/12356829
+# MYSQL 5.5.9 FOR MAC OSX HAS BUG WITH FOREIGN KEY CONSTRAINTS
+# This testcase is different from that for Bug#60196 in that the
+# referenced table contains a secondary key.  When the engine is
+# restarted, the referenced table is opened by the purge thread,
+# which does not notice that lower_case_table_names == 2.
+
+#
+# Create test data.
+#
+CREATE TABLE Bug_60309_FK (
+  ID INT PRIMARY KEY,
+  ID2 INT,
+   KEY K2(ID2)
+) ENGINE=InnoDB;
+CREATE TABLE Bug_60309 (
+  ID INT PRIMARY KEY,
+  FK_ID INT,
+  KEY (FK_ID),
+  CONSTRAINT FK FOREIGN KEY (FK_ID) REFERENCES Bug_60309_FK (ID)
+) ENGINE=InnoDB;
+
+INSERT INTO Bug_60309_FK (ID, ID2) VALUES (1, 1), (2, 2), (3, 3);
+INSERT INTO Bug_60309 VALUES (1, 1);
+--error ER_NO_REFERENCED_ROW_2
+INSERT INTO Bug_60309 VALUES (2, 99);
+
+SELECT * FROM Bug_60309_FK;
+SELECT * FROM Bug_60309;
+
+--echo # Stop server
+
+# Write file to make mysql-test-run.pl wait for the server to stop
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+# Send a shutdown request to the server
+-- shutdown_server 10
+
+# Call script that will poll the server waiting for it to disapear
+-- source include/wait_until_disconnected.inc
+
+--echo # Restart server.
+
+# Write file to make mysql-test-run.pl start up the server again
+--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+# Turn on reconnect
+--enable_reconnect
+
+# Call script that will poll the server waiting for it to be back online again
+--source include/wait_until_connected_again.inc
+
+# Turn off reconnect again
+--disable_reconnect
+
+--echo #
+--echo # Try to insert more to the example table with foreign keys.
+--echo # Bug60309 causes the foreign key file not to be found after
+--echo # the resstart above.
+--echo #
+SELECT * FROM Bug_60309;
+INSERT INTO Bug_60309 VALUES (2, 2);
+INSERT INTO Bug_60309 VALUES (3, 3);
+SELECT * FROM Bug_60309;
+
+--echo
+--echo # Clean up.
+DROP TABLE Bug_60309;
+DROP TABLE Bug_60309_FK;

=== modified file 'storage/innobase/dict/dict0dict.c'
--- a/storage/innobase/dict/dict0dict.c	revid:vasil.dimov@stripped
+++ b/storage/innobase/dict/dict0dict.c	revid:kevin.lewis@stripped
@@ -52,7 +52,6 @@ UNIV_INTERN dict_index_t*	dict_ind_compa
 #include "que0que.h"
 #include "rem0cmp.h"
 #include "row0merge.h"
-#include "srv0srv.h" /* srv_lower_case_table_names */
 #include "m_ctype.h" /* my_isspace() */
 #include "ha_prototypes.h" /* innobase_strcasecmp(), innobase_casedn_str()*/
 
@@ -3029,14 +3028,14 @@ dict_scan_table_name(
 	/* Values;  0 = Store and compare as given; case sensitive
 	            1 = Store and compare in lower; case insensitive
 	            2 = Store as given, compare in lower; case semi-sensitive */
-	if (srv_lower_case_table_names == 2) {
+	if (innobase_get_lower_case_table_names() == 2) {
 		innobase_casedn_str(ref);
 		*table = dict_table_get_low(ref);
 		memcpy(ref, database_name, database_name_len);
 		ref[database_name_len] = '/';
 		memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
 	} else {
-		if (srv_lower_case_table_names == 1) {
+		if (innobase_get_lower_case_table_names() == 1) {
 			innobase_casedn_str(ref);
 		}
 		*table = dict_table_get_low(ref);

=== modified file 'storage/innobase/dict/dict0load.c'
--- a/storage/innobase/dict/dict0load.c	revid:vasil.dimov@stripped
+++ b/storage/innobase/dict/dict0load.c	revid:kevin.lewis@stripped
@@ -2262,7 +2262,7 @@ loop:
 	may not be the same case, but the previous comparison showed that they
 	match with no-case.  */
 
-	if ((srv_lower_case_table_names != 2)
+	if ((innobase_get_lower_case_table_names() != 2)
 	    && (0 != ut_memcmp(field, table_name, len))) {
 		goto next_rec;
 	}

=== modified file 'storage/innobase/dict/dict0mem.c'
--- a/storage/innobase/dict/dict0mem.c	revid:vasil.dimov@stripped
+++ b/storage/innobase/dict/dict0mem.c	revid:kevin.lewis@stripped
@@ -33,7 +33,6 @@ Created 1/8/1996 Heikki Tuuri
 #include "data0type.h"
 #include "mach0data.h"
 #include "dict0dict.h"
-#include "srv0srv.h" /* srv_lower_case_table_names */
 #include "ha_prototypes.h" /* innobase_casedn_str()*/
 #ifndef UNIV_HOTBACKUP
 # include "lock0lock.h"
@@ -294,9 +293,9 @@ dict_mem_foreign_create(void)
 
 /**********************************************************************//**
 Sets the foreign_table_name_lookup pointer based on the value of
-srv_lower_case_table_names.  If that is 0 or 1, foreign_table_name_lookup
-will point to foreign_table_name.  If 2, then another string is allocated
-of the heap and set to lower case. */
+lower_case_table_names.  If that is 0 or 1, foreign_table_name_lookup
+will point to foreign_table_name.  If 2, then another string is
+allocated from foreign->heap and set to lower case. */
 UNIV_INTERN
 void
 dict_mem_foreign_table_name_lookup_set(
@@ -304,7 +303,7 @@ dict_mem_foreign_table_name_lookup_set(
 	dict_foreign_t*	foreign,	/*!< in/out: foreign struct */
 	ibool		do_alloc)	/*!< in: is an alloc needed */
 {
-	if (srv_lower_case_table_names == 2) {
+	if (innobase_get_lower_case_table_names() == 2) {
 		if (do_alloc) {
 			foreign->foreign_table_name_lookup = mem_heap_alloc(
 				foreign->heap,
@@ -321,9 +320,9 @@ dict_mem_foreign_table_name_lookup_set(
 
 /**********************************************************************//**
 Sets the referenced_table_name_lookup pointer based on the value of
-srv_lower_case_table_names.  If that is 0 or 1,
-referenced_table_name_lookup will point to referenced_table_name.  If 2,
-then another string is allocated of the heap and set to lower case. */
+lower_case_table_names.  If that is 0 or 1, referenced_table_name_lookup
+will point to referenced_table_name.  If 2, then another string is
+allocated from foreign->heap and set to lower case. */
 UNIV_INTERN
 void
 dict_mem_referenced_table_name_lookup_set(
@@ -331,7 +330,7 @@ dict_mem_referenced_table_name_lookup_se
 	dict_foreign_t*	foreign,	/*!< in/out: foreign struct */
 	ibool		do_alloc)	/*!< in: is an alloc needed */
 {
-	if (srv_lower_case_table_names == 2) {
+	if (innobase_get_lower_case_table_names() == 2) {
 		if (do_alloc) {
 			foreign->referenced_table_name_lookup = mem_heap_alloc(
 				foreign->heap,

=== modified file 'storage/innobase/handler/ha_innodb.cc'
--- a/storage/innobase/handler/ha_innodb.cc	revid:vasil.dimov@stripped
+++ b/storage/innobase/handler/ha_innodb.cc	revid:kevin.lewis@stripped
@@ -1199,6 +1199,20 @@ innobase_get_stmt(
 	return(stmt->str);
 }
 
+/**********************************************************************//**
+Get the current setting of the lower_case_table_names global parameter from
+mysqld.cc. We do a dirty read because for one there is no synchronization
+object and secondly there is little harm in doing so even if we get a torn
+read.
+@return	value of lower_case_table_names */
+extern "C" UNIV_INTERN
+ulint
+innobase_get_lower_case_table_names(void)
+/*=====================================*/
+{
+	return(lower_case_table_names);
+}
+
 #if defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN)
 extern MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list;
 /*******************************************************************//**
@@ -3671,7 +3685,6 @@ ha_innobase::open(
 	UT_NOT_USED(test_if_locked);
 
 	thd = ha_thd();
-	srv_lower_case_table_names = lower_case_table_names;
 
 	/* Under some cases MySQL seems to call this function while
 	holding btr_search_latch. This breaks the latching order as
@@ -6362,8 +6375,6 @@ err_col:
 			col_len);
 	}
 
-	srv_lower_case_table_names = lower_case_table_names;
-
 	error = row_create_table_for_mysql(table, trx);
 
 	if (error == DB_DUPLICATE_KEY) {
@@ -7223,8 +7234,6 @@ ha_innobase::delete_table(
 
 	/* Drop the table in InnoDB */
 
-	srv_lower_case_table_names = lower_case_table_names;
-
 	error = row_drop_table_for_mysql(norm_name, trx,
 					 thd_sql_command(thd)
 					 == SQLCOM_DROP_DB);
@@ -7354,8 +7363,6 @@ innobase_rename_table(
 		row_mysql_lock_data_dictionary(trx);
 	}
 
-	srv_lower_case_table_names = lower_case_table_names;
-
 	error = row_rename_table_for_mysql(
 		norm_from, norm_to, trx, lock_and_commit);
 

=== modified file 'storage/innobase/include/dict0mem.h'
--- a/storage/innobase/include/dict0mem.h	revid:vasil.dimov@stripped
+++ b/storage/innobase/include/dict0mem.h	revid:kevin.lewis@stripped
@@ -240,7 +240,9 @@ dict_mem_foreign_create(void);
 
 /**********************************************************************//**
 Sets the foreign_table_name_lookup pointer based on the value of
-srv_lower_case_table_names. */
+lower_case_table_names.  If that is 0 or 1, foreign_table_name_lookup
+will point to foreign_table_name.  If 2, then another string is
+allocated from the heap and set to lower case. */
 UNIV_INTERN
 void
 dict_mem_foreign_table_name_lookup_set(
@@ -249,8 +251,10 @@ dict_mem_foreign_table_name_lookup_set(
 	ibool		do_alloc);	/*!< in: is an alloc needed */
 
 /**********************************************************************//**
-Sets the reference_table_name_lookup pointer based on the value of
-srv_lower_case_table_names. */
+Sets the referenced_table_name_lookup pointer based on the value of
+lower_case_table_names.  If that is 0 or 1, referenced_table_name_lookup
+will point to referenced_table_name.  If 2, then another string is
+allocated from the heap and set to lower case. */
 UNIV_INTERN
 void
 dict_mem_referenced_table_name_lookup_set(

=== modified file 'storage/innobase/include/ha_prototypes.h'
--- a/storage/innobase/include/ha_prototypes.h	revid:vasil.dimov@stripped
+++ b/storage/innobase/include/ha_prototypes.h	revid:kevin.lewis@stripped
@@ -285,4 +285,15 @@ thd_set_lock_wait_time(
         void*   thd,	/*!< in: thread handle (THD*) */
         ulint   value);	/*!< in: time waited for the lock */
 
+/**********************************************************************//**
+Get the current setting of the lower_case_table_names global parameter from
+mysqld.cc. We do a dirty read because for one there is no synchronization
+object and secondly there is little harm in doing so even if we get a torn
+read.
+@return	value of lower_case_table_names */
+UNIV_INTERN
+ulint
+innobase_get_lower_case_table_names(void);
+/*=====================================*/
+
 #endif

=== modified file 'storage/innobase/include/srv0srv.h'
--- a/storage/innobase/include/srv0srv.h	revid:vasil.dimov@stripped
+++ b/storage/innobase/include/srv0srv.h	revid:kevin.lewis@stripped
@@ -71,9 +71,6 @@ at a time */
 #define SRV_AUTO_EXTEND_INCREMENT	\
 	(srv_auto_extend_increment * ((1024 * 1024) / UNIV_PAGE_SIZE))
 
-/* This is set to the MySQL server value for this variable. */
-extern uint	srv_lower_case_table_names;
-
 /* Mutex for locking srv_monitor_file */
 extern mutex_t	srv_monitor_file_mutex;
 /* Temporary file for innodb monitor output */

=== modified file 'storage/innobase/srv/srv0srv.c'
--- a/storage/innobase/srv/srv0srv.c	revid:vasil.dimov@stripped
+++ b/storage/innobase/srv/srv0srv.c	revid:kevin.lewis@stripped
@@ -86,12 +86,6 @@ Created 10/8/1995 Heikki Tuuri
 #include "mysql/plugin.h"
 #include "mysql/service_thd_wait.h"
 
-/* This is set to the MySQL server value for this variable.  It is only
-needed for FOREIGN KEY definition parsing since FOREIGN KEY names are not
-stored in the server metadata. The server stores and enforces it for
-regular database and table names.*/
-UNIV_INTERN uint	srv_lower_case_table_names	= 0;
-
 /* The following counter is incremented whenever there is some user activity
 in the server */
 UNIV_INTERN ulint	srv_activity_count	= 0;

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.5-innodb branch (kevin.lewis:3370 to 3371) Bug#60309Bug#12356829kevin.lewis26 Apr