From f8c904090a2cc4f571d98cfff554ffb0ff5684f0 Mon Sep 17 00:00:00 2001 From: Annamalai Gurusami <annamalai.gurusami@oracle.com> Date: Mon, 6 May 2013 20:31:26 +0530 Subject: [PATCH] Bug #16722314 FOREIGN KEY ID MODIFIED DURING EXPORT Bug #16754901 PARS_INFO_FREE NOT CALLED IN DICT_CREATE_ADD_FOREIGN_TO_DICTIONARY Merging the fix from mysql-5.5 to mysql-5.5.32-release. --- storage/innobase/dict/dict0crea.c | 59 ++++++++++--- storage/innobase/dict/dict0dict.c | 102 ++++++++++++++++++++--- storage/innobase/handler/ha_innodb.cc | 82 ++++++++++++++---- storage/innobase/include/dict0types.h | 5 +- storage/innobase/include/ha_prototypes.h | 22 ++++- storage/innobase/row/row0mysql.c | 31 +++++-- 6 files changed, 250 insertions(+), 51 deletions(-) diff --git a/storage/innobase/dict/dict0crea.c b/storage/innobase/dict/dict0crea.c index cb3dbcbe4a..bd4e449d11 100644 --- a/storage/innobase/dict/dict0crea.c +++ b/storage/innobase/dict/dict0crea.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -44,6 +44,21 @@ Created 1/8/1996 Heikki Tuuri #include "ut0vec.h" #include "ha_prototypes.h" +/************************************************************************* +Checks if a table name contains the string TEMP_TABLE_PATH_PREFIX which +denotes temporary tables in MySQL. */ +static +ibool +row_is_mysql_tmp_table_name( +/*========================*/ + /* out: TRUE if temporary table */ + const char* name) /* in: table name in the form + 'database/tablename' */ +{ + return(strstr(name, TEMP_TABLE_PATH_PREFIX) != NULL); +} + + /*****************************************************************//** Based on a table object, this function builds the entry to be inserted in the SYS_TABLES system table. @@ -1424,26 +1439,46 @@ dict_create_add_foreign_to_dictionary( { ulint error; ulint i; - - pars_info_t* info = pars_info_create(); + pars_info_t* info; if (foreign->id == NULL) { - char* stripped_name; /* Generate a new constraint id */ ulint namelen = strlen(table->name); char* id = mem_heap_alloc(foreign->heap, namelen + 20); - /* no overflow if number < 1e13 */ - sprintf(id, "%s_ibfk_%lu", table->name, (ulong) (*id_nr)++); - foreign->id = id; - stripped_name = strchr(foreign->id, '/') + 1; - if (innobase_check_identifier_length(stripped_name)) { - fprintf(stderr, "InnoDB: Generated foreign key " - "name (%s) is too long\n", foreign->id); - return(DB_IDENTIFIER_TOO_LONG); + if (row_is_mysql_tmp_table_name(table->name)) { + sprintf(id, "%s_ibfk_%lu", table->name, + (ulong) (*id_nr)++); + } else { + char table_name[MAX_TABLE_NAME_LEN + 20] = ""; + uint errors = 0; + + strncpy(table_name, table->name, + MAX_TABLE_NAME_LEN + 20); + + innobase_convert_to_system_charset( + strchr(table_name, '/') + 1, + strchr(table->name, '/') + 1, + MAX_TABLE_NAME_LEN, &errors); + + if (errors) { + strncpy(table_name, table->name, + MAX_TABLE_NAME_LEN + 20); + } + + sprintf(id, "%s_ibfk_%lu", table_name, + (ulong) (*id_nr)++); + + if (innobase_check_identifier_length( + strchr(id,'/') + 1)) { + return(DB_IDENTIFIER_TOO_LONG); + } } + foreign->id = id; } + info = pars_info_create(); + pars_info_add_str_literal(info, "id", foreign->id); pars_info_add_str_literal(info, "for_name", table->name); diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index 215ac35ae5..aec2264ad1 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -1116,22 +1116,78 @@ dict_table_rename_in_cache( dict_mem_foreign_table_name_lookup_set(foreign, FALSE); } if (strchr(foreign->id, '/')) { + /* This is a >= 4.0.18 format id */ + ulint db_len; char* old_id; + char old_name_cs_filename[MAX_TABLE_NAME_LEN+20]; + uint errors = 0; + + /* All table names are internally stored in charset + my_charset_filename (except the temp tables and the + partition identifier suffix in partition tables). The + foreign key constraint names are internally stored + in UTF-8 charset. The variable fkid here is used + to store foreign key constraint name in charset + my_charset_filename for comparison further below. */ + char fkid[MAX_TABLE_NAME_LEN+20]; + ibool on_tmp = FALSE; + + /* The old table name in my_charset_filename is stored + in old_name_cs_filename */ + + strncpy(old_name_cs_filename, old_name, + MAX_TABLE_NAME_LEN); + if (strstr(old_name, TEMP_TABLE_PATH_PREFIX) == NULL) { + + innobase_convert_to_system_charset( + strchr(old_name_cs_filename, '/') + 1, + strchr(old_name, '/') + 1, + MAX_TABLE_NAME_LEN, &errors); + + if (errors) { + /* There has been an error to convert + old table into UTF-8. This probably + means that the old table name is + actually in UTF-8. */ + innobase_convert_to_filename_charset( + strchr(old_name_cs_filename, + '/') + 1, + strchr(old_name, '/') + 1, + MAX_TABLE_NAME_LEN); + } else { + /* Old name already in + my_charset_filename */ + strncpy(old_name_cs_filename, old_name, + MAX_TABLE_NAME_LEN); + } + } - /* This is a >= 4.0.18 format id */ + strncpy(fkid, foreign->id, MAX_TABLE_NAME_LEN); + + if (strstr(fkid, TEMP_TABLE_PATH_PREFIX) == NULL) { + innobase_convert_to_filename_charset( + strchr(fkid, '/') + 1, + strchr(foreign->id, '/') + 1, + MAX_TABLE_NAME_LEN+20); + } else { + on_tmp = TRUE; + } old_id = mem_strdup(foreign->id); - if (ut_strlen(foreign->id) > ut_strlen(old_name) + if (ut_strlen(fkid) > ut_strlen(old_name_cs_filename) + ((sizeof dict_ibfk) - 1) - && !memcmp(foreign->id, old_name, - ut_strlen(old_name)) - && !memcmp(foreign->id + ut_strlen(old_name), + && !memcmp(fkid, old_name_cs_filename, + ut_strlen(old_name_cs_filename)) + && !memcmp(fkid + ut_strlen(old_name_cs_filename), dict_ibfk, (sizeof dict_ibfk) - 1)) { /* This is a generated >= 4.0.18 format id */ + char table_name[MAX_TABLE_NAME_LEN] = ""; + uint errors = 0; + if (strlen(table->name) > strlen(old_name)) { foreign->id = mem_heap_alloc( foreign->heap, @@ -1139,11 +1195,36 @@ dict_table_rename_in_cache( + strlen(old_id) + 1); } + /* Convert the table name to UTF-8 */ + strncpy(table_name, table->name, + MAX_TABLE_NAME_LEN); + innobase_convert_to_system_charset( + strchr(table_name, '/') + 1, + strchr(table->name, '/') + 1, + MAX_TABLE_NAME_LEN, &errors); + + if (errors) { + /* Table name could not be converted + from charset my_charset_filename to + UTF-8. This means that the table name + is already in UTF-8 (#mysql#50). */ + strncpy(table_name, table->name, + MAX_TABLE_NAME_LEN); + } + /* Replace the prefix 'databasename/tablename' with the new names */ - strcpy(foreign->id, table->name); - strcat(foreign->id, - old_id + ut_strlen(old_name)); + strcpy(foreign->id, table_name); + if (on_tmp) { + strcat(foreign->id, + old_id + ut_strlen(old_name)); + } else { + sprintf(strchr(foreign->id, '/') + 1, + "%s%s", + strchr(table_name, '/') +1, + strstr(old_id, "_ibfk_") ); + } + } else { /* This is a >= 4.0.18 format id where the user gave the id name */ @@ -4691,7 +4772,6 @@ dict_print_info_on_foreign_key_in_create_format( dict_foreign_t* foreign, /*!< in: foreign key constraint */ ibool add_newline) /*!< in: whether to add a newline */ { - char constraint_name[MAX_TABLE_NAME_LEN]; const char* stripped_id; ulint i; @@ -4713,9 +4793,7 @@ dict_print_info_on_foreign_key_in_create_format( } fputs(" CONSTRAINT ", file); - innobase_convert_from_id(&my_charset_filename, constraint_name, - stripped_id, MAX_TABLE_NAME_LEN); - ut_print_name(file, trx, FALSE, constraint_name); + ut_print_name(file, trx, FALSE, stripped_id); fputs(" FOREIGN KEY (", file); for (i = 0;;) { diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index fea4ae05ae..5df84d7cd0 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1159,33 +1159,27 @@ innobase_convert_from_table_id( /********************************************************************** Check if the length of the identifier exceeds the maximum allowed. -The input to this function is an identifier in charset my_charset_filename. return true when length of identifier is too long. */ -extern "C" UNIV_INTERN +extern "C" my_bool innobase_check_identifier_length( /*=============================*/ - const char* id) /* in: identifier to check. it must belong - to charset my_charset_filename */ + const char* id) /* in: FK identifier to check excluding the + database portion. */ { - char tmp[MAX_TABLE_NAME_LEN + 10]; - uint errors; - uint len; int well_formed_error = 0; - CHARSET_INFO* cs1 = &my_charset_filename; - CHARSET_INFO* cs2 = thd_charset(current_thd); + CHARSET_INFO *cs = system_charset_info; + DBUG_ENTER("innobase_check_identifier_length"); - len = strconvert(cs1, id, cs2, tmp, MAX_TABLE_NAME_LEN + 10, &errors); + uint res = cs->cset->well_formed_len(cs, id, id + strlen(id), + NAME_CHAR_LEN, + &well_formed_error); - uint res = cs2->cset->well_formed_len(cs2, tmp, tmp + len, - NAME_CHAR_LEN, - &well_formed_error); - - if (well_formed_error || res != len) { - my_error(ER_TOO_LONG_IDENT, MYF(0), tmp); - return(true); + if (well_formed_error || res == NAME_CHAR_LEN) { + my_error(ER_TOO_LONG_IDENT, MYF(0), id); + DBUG_RETURN(true); } - return(false); + DBUG_RETURN(false); } /******************************************************************//** @@ -11966,3 +11960,55 @@ test_innobase_convert_name() } #endif /* UNIV_COMPILE_TEST_FUNCS */ + +/********************************************************************** +Converts an identifier from my_charset_filename to UTF-8 charset. */ +extern "C" +uint +innobase_convert_to_filename_charset( +/*=================================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len) /* in: length of 'to', in bytes */ +{ + uint errors; + uint rlen; + CHARSET_INFO* cs_to = &my_charset_filename; + CHARSET_INFO* cs_from = system_charset_info; + + rlen = strconvert(cs_from, from, cs_to, to, len, &errors); + + if (errors) { + fprintf(stderr, "InnoDB: There was a problem in converting" + "'%s' in charset %s to charset %s", from, cs_from->name, + cs_to->name); + } + + return(rlen); +} + +/********************************************************************** +Converts an identifier from my_charset_filename to UTF-8 charset. */ +extern "C" +uint +innobase_convert_to_system_charset( +/*===============================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len, /* in: length of 'to', in bytes */ + uint* errors) /* out: error return */ +{ + uint rlen; + CHARSET_INFO* cs1 = &my_charset_filename; + CHARSET_INFO* cs2 = system_charset_info; + + rlen = strconvert(cs1, from, cs2, to, len, errors); + + if (*errors) { + fprintf(stderr, "InnoDB: There was a problem in converting" + "'%s' in charset %s to charset %s", from, cs1->name, + cs2->name); + } + + return(rlen); +} diff --git a/storage/innobase/include/dict0types.h b/storage/innobase/include/dict0types.h index f0a05a3807..8e3a04f795 100644 --- a/storage/innobase/include/dict0types.h +++ b/storage/innobase/include/dict0types.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. +Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -58,4 +58,7 @@ enum dict_err_ignore { typedef enum dict_err_ignore dict_err_ignore_t; +#define TEMP_TABLE_PREFIX "#sql" +#define TEMP_TABLE_PATH_PREFIX "/" TEMP_TABLE_PREFIX + #endif diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index ae0188fdaa..dc730f9b6b 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2006, 2010, Innobase Oy. All Rights Reserved. +Copyright (c) 2000, 2013, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -307,4 +307,24 @@ innobase_check_identifier_length( const char* id); /* in: identifier to check. it must belong to charset my_charset_filename */ +/********************************************************************** +Converts an identifier from my_charset_filename to UTF-8 charset. */ +uint +innobase_convert_to_system_charset( +/*===============================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len, /* in: length of 'to', in bytes */ + uint* errors); /* out: error return */ + +/********************************************************************** +Converts an identifier from my_charset_filename to UTF-8 charset. */ +uint +innobase_convert_to_filename_charset( +/*=================================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len); /* in: length of 'to', in bytes */ + + #endif diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index d97476dcdb..ff8f79f4f3 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -53,7 +53,7 @@ Created 9/17/2000 Heikki Tuuri #include "ibuf0ibuf.h" #include "m_string.h" #include "my_sys.h" - +#include "ha_prototypes.h" /** Provide optional 4.x backwards compatibility for 5.0 and above */ UNIV_INTERN ibool row_rollback_on_timeout = FALSE; @@ -3956,12 +3956,29 @@ row_rename_table_for_mysql( goto end; } else if (!new_is_tmp) { /* Rename all constraints. */ + char new_table_name[MAX_TABLE_NAME_LEN] = ""; + uint errors = 0; info = pars_info_create(); pars_info_add_str_literal(info, "new_table_name", new_name); pars_info_add_str_literal(info, "old_table_name", old_name); + strncpy(new_table_name, new_name, MAX_TABLE_NAME_LEN); + innobase_convert_to_system_charset( + strchr(new_table_name, '/') + 1, + strchr(new_name, '/') +1, + MAX_TABLE_NAME_LEN, &errors); + + if (errors) { + /* Table name could not be converted from charset + my_charset_filename to UTF-8. This means that the + table name is already in UTF-8 (#mysql#50). */ + strncpy(new_table_name, new_name, MAX_TABLE_NAME_LEN); + } + + pars_info_add_str_literal(info, "new_table_utf8", new_table_name); + err = que_eval_sql( info, "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n" @@ -3973,6 +3990,7 @@ row_rename_table_for_mysql( "old_t_name_len INT;\n" "new_db_name_len INT;\n" "id_len INT;\n" + "offset INT;\n" "found INT;\n" "BEGIN\n" "found := 1;\n" @@ -3981,8 +3999,6 @@ row_rename_table_for_mysql( "new_db_name := SUBSTR(:new_table_name, 0,\n" " new_db_name_len);\n" "old_t_name_len := LENGTH(:old_table_name);\n" - "gen_constr_prefix := CONCAT(:old_table_name,\n" - " '_ibfk_');\n" "WHILE found = 1 LOOP\n" " SELECT ID INTO foreign_id\n" " FROM SYS_FOREIGN\n" @@ -3999,12 +4015,13 @@ row_rename_table_for_mysql( " id_len := LENGTH(foreign_id);\n" " IF (INSTR(foreign_id, '/') > 0) THEN\n" " IF (INSTR(foreign_id,\n" - " gen_constr_prefix) > 0)\n" + " '_ibfk_') > 0)\n" " THEN\n" + " offset := INSTR(foreign_id, '_ibfk_') - 1;\n" " new_foreign_id :=\n" - " CONCAT(:new_table_name,\n" - " SUBSTR(foreign_id, old_t_name_len,\n" - " id_len - old_t_name_len));\n" + " CONCAT(:new_table_utf8,\n" + " SUBSTR(foreign_id, offset,\n" + " id_len - offset));\n" " ELSE\n" " new_foreign_id :=\n" " CONCAT(new_db_name,\n" -- 2.30.9