mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
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.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -52,7 +52,6 @@ UNIV_INTERN dict_index_t* dict_ind_compact;
|
||||
#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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_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->referenced_table_name_lookup = mem_heap_alloc(
|
||||
foreign->heap,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user