Commit 7163d59f authored by osku's avatar osku

Forward-port r46 from branches/5.0:

Mostly fix bug #13778, when FOREIGN_KEY_CHECKS=0 we still need to check
that datatypes between foreign key references are compatible.

Add test cases (also for bug #9802).
parent dfa8edb5
...@@ -2104,8 +2104,11 @@ dict_foreign_find_index( ...@@ -2104,8 +2104,11 @@ dict_foreign_find_index(
dict_table_t* table, /* in: table */ dict_table_t* table, /* in: table */
const char** columns,/* in: array of column names */ const char** columns,/* in: array of column names */
ulint n_cols, /* in: number of columns */ ulint n_cols, /* in: number of columns */
dict_index_t* types_idx)/* in: NULL or an index to whose types the dict_index_t* types_idx, /* in: NULL or an index to whose types the
column types must match */ column types must match */
ibool check_charsets) /* in: whether to check charsets.
only has an effect if types_idx !=
NULL. */
{ {
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
dict_index_t* index; dict_index_t* index;
...@@ -2135,7 +2138,8 @@ dict_foreign_find_index( ...@@ -2135,7 +2138,8 @@ dict_foreign_find_index(
if (types_idx && !cmp_types_are_equal( if (types_idx && !cmp_types_are_equal(
dict_index_get_nth_type(index, i), dict_index_get_nth_type(index, i),
dict_index_get_nth_type(types_idx, i))) { dict_index_get_nth_type(types_idx, i),
check_charsets)) {
break; break;
} }
...@@ -2212,7 +2216,8 @@ dict_foreign_add_to_cache( ...@@ -2212,7 +2216,8 @@ dict_foreign_add_to_cache(
/*======================*/ /*======================*/
/* out: DB_SUCCESS or error code */ /* out: DB_SUCCESS or error code */
dict_foreign_t* foreign, /* in, own: foreign key constraint */ dict_foreign_t* foreign, /* in, own: foreign key constraint */
ibool check_types) /* in: TRUE=check type compatibility */ ibool check_charsets) /* in: TRUE=check charset
compatibility */
{ {
dict_table_t* for_table; dict_table_t* for_table;
dict_table_t* ref_table; dict_table_t* ref_table;
...@@ -2248,16 +2253,10 @@ dict_foreign_add_to_cache( ...@@ -2248,16 +2253,10 @@ dict_foreign_add_to_cache(
} }
if (for_in_cache->referenced_table == NULL && ref_table) { if (for_in_cache->referenced_table == NULL && ref_table) {
dict_index_t* types_idx;
if (check_types) {
types_idx = for_in_cache->foreign_index;
} else {
types_idx = NULL;
}
index = dict_foreign_find_index(ref_table, index = dict_foreign_find_index(ref_table,
(const char**) for_in_cache->referenced_col_names, (const char**) for_in_cache->referenced_col_names,
for_in_cache->n_fields, for_in_cache->n_fields,
types_idx); for_in_cache->foreign_index, check_charsets);
if (index == NULL) { if (index == NULL) {
dict_foreign_error_report(ef, for_in_cache, dict_foreign_error_report(ef, for_in_cache,
...@@ -2281,16 +2280,10 @@ dict_foreign_add_to_cache( ...@@ -2281,16 +2280,10 @@ dict_foreign_add_to_cache(
} }
if (for_in_cache->foreign_table == NULL && for_table) { if (for_in_cache->foreign_table == NULL && for_table) {
dict_index_t* types_idx;
if (check_types) {
types_idx = for_in_cache->referenced_index;
} else {
types_idx = NULL;
}
index = dict_foreign_find_index(for_table, index = dict_foreign_find_index(for_table,
(const char**) for_in_cache->foreign_col_names, (const char**) for_in_cache->foreign_col_names,
for_in_cache->n_fields, for_in_cache->n_fields,
types_idx); for_in_cache->referenced_index, check_charsets);
if (index == NULL) { if (index == NULL) {
dict_foreign_error_report(ef, for_in_cache, dict_foreign_error_report(ef, for_in_cache,
...@@ -3097,7 +3090,7 @@ col_loop1: ...@@ -3097,7 +3090,7 @@ col_loop1:
/* Try to find an index which contains the columns /* Try to find an index which contains the columns
as the first fields and in the right order */ as the first fields and in the right order */
index = dict_foreign_find_index(table, column_names, i, NULL); index = dict_foreign_find_index(table, column_names, i, NULL, TRUE);
if (!index) { if (!index) {
mutex_enter(&dict_foreign_err_mutex); mutex_enter(&dict_foreign_err_mutex);
...@@ -3362,8 +3355,7 @@ try_find_index: ...@@ -3362,8 +3355,7 @@ try_find_index:
if (referenced_table) { if (referenced_table) {
index = dict_foreign_find_index(referenced_table, index = dict_foreign_find_index(referenced_table,
column_names, i, column_names, i, foreign->foreign_index, TRUE);
foreign->foreign_index);
if (!index) { if (!index) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex); mutex_enter(&dict_foreign_err_mutex);
......
...@@ -1091,7 +1091,7 @@ dict_load_foreign( ...@@ -1091,7 +1091,7 @@ dict_load_foreign(
/* out: DB_SUCCESS or error code */ /* out: DB_SUCCESS or error code */
const char* id, /* in: foreign constraint id as a const char* id, /* in: foreign constraint id as a
null-terminated string */ null-terminated string */
ibool check_types)/* in: TRUE=check type compatibility */ ibool check_charsets)/* in: TRUE=check charset compatibility */
{ {
dict_foreign_t* foreign; dict_foreign_t* foreign;
dict_table_t* sys_foreign; dict_table_t* sys_foreign;
...@@ -1204,7 +1204,7 @@ dict_load_foreign( ...@@ -1204,7 +1204,7 @@ dict_load_foreign(
a new foreign key constraint but loading one from the data a new foreign key constraint but loading one from the data
dictionary. */ dictionary. */
return(dict_foreign_add_to_cache(foreign, check_types)); return(dict_foreign_add_to_cache(foreign, check_charsets));
} }
/*************************************************************************** /***************************************************************************
...@@ -1219,7 +1219,8 @@ dict_load_foreigns( ...@@ -1219,7 +1219,8 @@ dict_load_foreigns(
/*===============*/ /*===============*/
/* out: DB_SUCCESS or error code */ /* out: DB_SUCCESS or error code */
const char* table_name, /* in: table name */ const char* table_name, /* in: table name */
ibool check_types) /* in: TRUE=check type compatibility */ ibool check_charsets) /* in: TRUE=check charset
compatibility */
{ {
btr_pcur_t pcur; btr_pcur_t pcur;
mem_heap_t* heap; mem_heap_t* heap;
...@@ -1319,7 +1320,7 @@ loop: ...@@ -1319,7 +1320,7 @@ loop:
/* Load the foreign constraint definition to the dictionary cache */ /* Load the foreign constraint definition to the dictionary cache */
err = dict_load_foreign(id, check_types); err = dict_load_foreign(id, check_charsets);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
btr_pcur_close(&pcur); btr_pcur_close(&pcur);
......
...@@ -197,7 +197,8 @@ dict_foreign_add_to_cache( ...@@ -197,7 +197,8 @@ dict_foreign_add_to_cache(
/*======================*/ /*======================*/
/* out: DB_SUCCESS or error code */ /* out: DB_SUCCESS or error code */
dict_foreign_t* foreign, /* in, own: foreign key constraint */ dict_foreign_t* foreign, /* in, own: foreign key constraint */
ibool check_types); /* in: TRUE=check type compatibility */ ibool check_charsets);/* in: TRUE=check charset
compatibility */
/************************************************************************* /*************************************************************************
Checks if a table is referenced by foreign keys. */ Checks if a table is referenced by foreign keys. */
......
...@@ -82,7 +82,8 @@ dict_load_foreigns( ...@@ -82,7 +82,8 @@ dict_load_foreigns(
/*===============*/ /*===============*/
/* out: DB_SUCCESS or error code */ /* out: DB_SUCCESS or error code */
const char* table_name, /* in: table name */ const char* table_name, /* in: table name */
ibool check_types); /* in: TRUE=check type compatibility */ ibool check_charsets);/* in: TRUE=check charsets
compatibility */
/************************************************************************ /************************************************************************
Prints to the standard output information on all tables found in the data Prints to the standard output information on all tables found in the data
dictionary system table. */ dictionary system table. */
......
...@@ -24,7 +24,8 @@ cmp_types_are_equal( ...@@ -24,7 +24,8 @@ cmp_types_are_equal(
/* out: TRUE if the types are considered /* out: TRUE if the types are considered
equal in comparisons */ equal in comparisons */
dtype_t* type1, /* in: type 1 */ dtype_t* type1, /* in: type 1 */
dtype_t* type2); /* in: type 2 */ dtype_t* type2, /* in: type 2 */
ibool check_charsets); /* in: whether to check charsets */
/***************************************************************** /*****************************************************************
This function is used to compare two data fields for which we know the This function is used to compare two data fields for which we know the
data type. */ data type. */
......
...@@ -2801,3 +2801,35 @@ insert into t2 values (4,_ucs2 0x05612020,_ucs2 0x05612020,'taken'); ...@@ -2801,3 +2801,35 @@ insert into t2 values (4,_ucs2 0x05612020,_ucs2 0x05612020,'taken');
drop table t1; drop table t1;
drop table t2; drop table t2;
commit; commit;
set foreign_key_checks=0;
create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb;
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
ERROR HY000: Can't create table './test/t1.frm' (errno: 150)
set foreign_key_checks=1;
drop table t2;
set foreign_key_checks=0;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8;
ERROR HY000: Can't create table './test/t2.frm' (errno: 150)
set foreign_key_checks=1;
drop table t1;
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb;
create table t1(a varchar(10) primary key) engine = innodb;
alter table t1 modify column a int;
Got one of the listed errors
set foreign_key_checks=1;
drop table t2,t1;
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
alter table t1 convert to character set utf8;
set foreign_key_checks=1;
drop table t2,t1;
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t3(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=utf8;
rename table t3 to t1;
ERROR HY000: Error on rename of './test/t3' to './test/t1' (errno: 150)
set foreign_key_checks=1;
drop table t2,t3;
...@@ -1765,3 +1765,53 @@ insert into t2 values (4,_ucs2 0x05612020,_ucs2 0x05612020,'taken'); ...@@ -1765,3 +1765,53 @@ insert into t2 values (4,_ucs2 0x05612020,_ucs2 0x05612020,'taken');
drop table t1; drop table t1;
drop table t2; drop table t2;
commit; commit;
# tests for bugs #9802 and #13778
# test that FKs between invalid types are not accepted
set foreign_key_checks=0;
create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb;
-- error 1005
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
set foreign_key_checks=1;
drop table t2;
# test that FKs between different charsets are not accepted in CREATE even
# when f_k_c is 0
set foreign_key_checks=0;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
-- error 1005
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8;
set foreign_key_checks=1;
drop table t1;
# test that invalid datatype conversions with ALTER are not allowed
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb;
create table t1(a varchar(10) primary key) engine = innodb;
-- error 1025,1025
alter table t1 modify column a int;
set foreign_key_checks=1;
drop table t2,t1;
# test that charset conversions with ALTER are allowed when f_k_c is 0
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
alter table t1 convert to character set utf8;
set foreign_key_checks=1;
drop table t2,t1;
# test that RENAME does not allow invalid charsets when f_k_c is 0
set foreign_key_checks=0;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1;
create table t3(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=utf8;
-- error 1025
rename table t3 to t1;
set foreign_key_checks=1;
drop table t2,t3;
...@@ -99,7 +99,8 @@ cmp_types_are_equal( ...@@ -99,7 +99,8 @@ cmp_types_are_equal(
/* out: TRUE if the types are considered /* out: TRUE if the types are considered
equal in comparisons */ equal in comparisons */
dtype_t* type1, /* in: type 1 */ dtype_t* type1, /* in: type 1 */
dtype_t* type2) /* in: type 2 */ dtype_t* type2, /* in: type 2 */
ibool check_charsets) /* in: whether to check charsets */
{ {
if (dtype_is_non_binary_string_type(type1->mtype, type1->prtype) if (dtype_is_non_binary_string_type(type1->mtype, type1->prtype)
&& dtype_is_non_binary_string_type(type2->mtype, type2->prtype)) { && dtype_is_non_binary_string_type(type2->mtype, type2->prtype)) {
...@@ -107,12 +108,12 @@ cmp_types_are_equal( ...@@ -107,12 +108,12 @@ cmp_types_are_equal(
/* Both are non-binary string types: they can be compared if /* Both are non-binary string types: they can be compared if
and only if the charset-collation is the same */ and only if the charset-collation is the same */
if (dtype_get_charset_coll(type1->prtype) if (check_charsets) {
== dtype_get_charset_coll(type2->prtype)) { return(dtype_get_charset_coll(type1->prtype)
== dtype_get_charset_coll(type2->prtype));
} else {
return(TRUE); return(TRUE);
} }
return(FALSE);
} }
if (dtype_is_binary_string_type(type1->mtype, type1->prtype) if (dtype_is_binary_string_type(type1->mtype, type1->prtype)
......
...@@ -2132,7 +2132,7 @@ row_table_add_foreign_constraints( ...@@ -2132,7 +2132,7 @@ row_table_add_foreign_constraints(
if (err == DB_SUCCESS) { if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */ /* Check that also referencing constraints are ok */
err = dict_load_foreigns(name, trx->check_foreigns); err = dict_load_foreigns(name, TRUE);
} }
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
...@@ -3590,7 +3590,8 @@ row_rename_table_for_mysql( ...@@ -3590,7 +3590,8 @@ row_rename_table_for_mysql(
mem_heap_t* heap = NULL; mem_heap_t* heap = NULL;
const char** constraints_to_drop = NULL; const char** constraints_to_drop = NULL;
ulint n_constraints_to_drop = 0; ulint n_constraints_to_drop = 0;
ibool recovering_temp_table = FALSE; ibool recovering_temp_table = FALSE;
ibool old_is_tmp, new_is_tmp;
ulint len; ulint len;
ulint i; ulint i;
ibool success; ibool success;
...@@ -3630,6 +3631,9 @@ row_rename_table_for_mysql( ...@@ -3630,6 +3631,9 @@ row_rename_table_for_mysql(
trx->op_info = "renaming table"; trx->op_info = "renaming table";
trx_start_if_not_started(trx); trx_start_if_not_started(trx);
old_is_tmp = row_is_mysql_tmp_table_name(old_name);
new_is_tmp = row_is_mysql_tmp_table_name(new_name);
if (row_mysql_is_recovered_tmp_table(new_name)) { if (row_mysql_is_recovered_tmp_table(new_name)) {
recovering_temp_table = TRUE; recovering_temp_table = TRUE;
...@@ -3676,7 +3680,7 @@ row_rename_table_for_mysql( ...@@ -3676,7 +3680,7 @@ row_rename_table_for_mysql(
len = (sizeof str1) + (sizeof str2) + (sizeof str3) + (sizeof str5) - 4 len = (sizeof str1) + (sizeof str2) + (sizeof str3) + (sizeof str5) - 4
+ ut_strlenq(new_name, '\'') + ut_strlenq(old_name, '\''); + ut_strlenq(new_name, '\'') + ut_strlenq(old_name, '\'');
if (row_is_mysql_tmp_table_name(new_name)) { if (new_is_tmp) {
db_name_len = dict_get_db_name_len(old_name) + 1; db_name_len = dict_get_db_name_len(old_name) + 1;
/* MySQL is doing an ALTER TABLE command and it renames the /* MySQL is doing an ALTER TABLE command and it renames the
...@@ -3829,7 +3833,7 @@ row_rename_table_for_mysql( ...@@ -3829,7 +3833,7 @@ row_rename_table_for_mysql(
the table is stored in a single-table tablespace */ the table is stored in a single-table tablespace */
success = dict_table_rename_in_cache(table, new_name, success = dict_table_rename_in_cache(table, new_name,
!row_is_mysql_tmp_table_name(new_name)); !new_is_tmp);
if (!success) { if (!success) {
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL); trx_general_rollback_for_mysql(trx, FALSE, NULL);
...@@ -3846,19 +3850,16 @@ row_rename_table_for_mysql( ...@@ -3846,19 +3850,16 @@ row_rename_table_for_mysql(
goto funct_exit; goto funct_exit;
} }
err = dict_load_foreigns(new_name, trx->check_foreigns); /* We only want to switch off some of the type checking in
an ALTER, not in a RENAME. */
if (row_is_mysql_tmp_table_name(old_name)) {
err = dict_load_foreigns(new_name,
old_is_tmp ? trx->check_foreigns : TRUE);
/* MySQL is doing an ALTER TABLE command and it if (err != DB_SUCCESS) {
renames the created temporary table to the name ut_print_timestamp(stderr);
of the original table. In the ALTER TABLE we maybe
created some FOREIGN KEY constraints for the temporary
table. But we want to load also the foreign key
constraint definitions for the original table name. */
if (err != DB_SUCCESS) { if (old_is_tmp) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: in ALTER TABLE ", fputs(" InnoDB: Error: in ALTER TABLE ",
stderr); stderr);
ut_print_name(stderr, trx, new_name); ut_print_name(stderr, trx, new_name);
...@@ -3866,36 +3867,23 @@ row_rename_table_for_mysql( ...@@ -3866,36 +3867,23 @@ row_rename_table_for_mysql(
"InnoDB: has or is referenced in foreign key constraints\n" "InnoDB: has or is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n", "InnoDB: which are not compatible with the new table definition.\n",
stderr); stderr);
} else {
ut_a(dict_table_rename_in_cache(table,
old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
NULL);
trx->error_state = DB_SUCCESS;
}
} else {
if (err != DB_SUCCESS) {
ut_print_timestamp(stderr);
fputs( fputs(
" InnoDB: Error: in RENAME TABLE table ", " InnoDB: Error: in RENAME TABLE table ",
stderr); stderr);
ut_print_name(stderr, trx, new_name); ut_print_name(stderr, trx, new_name);
fputs("\n" fputs("\n"
"InnoDB: is referenced in foreign key constraints\n" "InnoDB: is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n", "InnoDB: which are not compatible with the new table definition.\n",
stderr); stderr);
ut_a(dict_table_rename_in_cache(table,
old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
NULL);
trx->error_state = DB_SUCCESS;
} }
ut_a(dict_table_rename_in_cache(table,
old_name, FALSE));
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE,
NULL);
trx->error_state = DB_SUCCESS;
} }
} }
funct_exit: funct_exit:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment