Many files:

  Fix bug #2167: generate foreign key id's locally for each table, in the form databasename/tablename_ibfk_number; if the user gives the constraint name explicitly remember it; these changes should ensure that foreign key id's in a slave are the same as in the master, and DROP FOREIGN KEY does not break replication
sync0sync.c:
  UNIV_SYNC_DEBUG caused assertion in the creation of the doublewrite buffer, if we do not allow thousands of latches per thread
parent d7b9d5c9
...@@ -1171,23 +1171,36 @@ dict_create_or_check_foreign_constraint_tables(void) ...@@ -1171,23 +1171,36 @@ dict_create_or_check_foreign_constraint_tables(void)
} }
/************************************************************************ /************************************************************************
Adds foreign key definitions to data dictionary tables in the database. */ Adds foreign key definitions to data dictionary tables in the database. We
look at table->foreign_list, and also generate names to constraints that were
not named by the user. A generated constraint has a name of the format
databasename/tablename_ibfk_<number>, where the numbers start from 1, and are
given locally for this table, that is, the number is not global, as in the
old format constraints < 4.0.18 it used to be. */
ulint ulint
dict_create_add_foreigns_to_dictionary( dict_create_add_foreigns_to_dictionary(
/*===================================*/ /*===================================*/
/* out: error code or DB_SUCCESS */ /* out: error code or DB_SUCCESS */
ulint start_id,/* in: if we are actually doing ALTER TABLE
ADD CONSTRAINT, we want to generate constraint
numbers which are bigger than in the table so
far; we number the constraints from
start_id + 1 up; start_id should be set to 0 if
we are creating a new table, or if the table
so far has no constraints for which the name
was generated here */
dict_table_t* table, /* in: table */ dict_table_t* table, /* in: table */
trx_t* trx) /* in: transaction */ trx_t* trx) /* in: transaction */
{ {
dict_foreign_t* foreign; dict_foreign_t* foreign;
que_thr_t* thr; que_thr_t* thr;
que_t* graph; que_t* graph;
dulint id; ulint number = start_id + 1;
ulint len; ulint len;
ulint error; ulint error;
char* ebuf = dict_foreign_err_buf;
ulint i; ulint i;
char buf2[50];
char buf[10000]; char buf[10000];
ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(mutex_own(&(dict_sys->mutex)));
...@@ -1215,18 +1228,18 @@ loop: ...@@ -1215,18 +1228,18 @@ loop:
"PROCEDURE ADD_FOREIGN_DEFS_PROC () IS\n" "PROCEDURE ADD_FOREIGN_DEFS_PROC () IS\n"
"BEGIN\n"); "BEGIN\n");
/* We allocate the new id from the sequence of table id's */ if (foreign->id == NULL) {
id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); /* Generate a new constraint id */
foreign->id = mem_heap_alloc(foreign->heap,
sprintf(buf2, "%lu_%lu", ut_dulint_get_high(id), ut_strlen(table->name)
ut_dulint_get_low(id)); + 20);
foreign->id = mem_heap_alloc(foreign->heap, ut_strlen(buf2) + 1); sprintf(foreign->id, "%s_ibfk_%lu", table->name, number);
ut_memcpy(foreign->id, buf2, ut_strlen(buf2) + 1); number++;
}
len += sprintf(buf + len, len += sprintf(buf + len,
"INSERT INTO SYS_FOREIGN VALUES('%lu_%lu', '%s', '%s', %lu);\n", "INSERT INTO SYS_FOREIGN VALUES('%s', '%s', '%s', %lu);\n",
ut_dulint_get_high(id), foreign->id,
ut_dulint_get_low(id),
table->name, table->name,
foreign->referenced_table_name, foreign->referenced_table_name,
foreign->n_fields foreign->n_fields
...@@ -1235,9 +1248,8 @@ loop: ...@@ -1235,9 +1248,8 @@ loop:
for (i = 0; i < foreign->n_fields; i++) { for (i = 0; i < foreign->n_fields; i++) {
len += sprintf(buf + len, len += sprintf(buf + len,
"INSERT INTO SYS_FOREIGN_COLS VALUES('%lu_%lu', %lu, '%s', '%s');\n", "INSERT INTO SYS_FOREIGN_COLS VALUES('%s', %lu, '%s', '%s');\n",
ut_dulint_get_high(id), foreign->id,
ut_dulint_get_low(id),
i, i,
foreign->foreign_col_names[i], foreign->foreign_col_names[i],
foreign->referenced_col_names[i]); foreign->referenced_col_names[i]);
...@@ -1262,29 +1274,30 @@ loop: ...@@ -1262,29 +1274,30 @@ loop:
que_graph_free(graph); que_graph_free(graph);
if (error != DB_SUCCESS) {
fprintf(stderr,
"InnoDB: Foreign key constraint creation failed:\n"
"InnoDB: internal error number %lu\n", error);
if (error == DB_DUPLICATE_KEY) { if (error == DB_DUPLICATE_KEY) {
fprintf(stderr, mutex_enter(&dict_foreign_err_mutex);
"InnoDB: Duplicate key error in system table %s index %s\n", ut_sprintf_timestamp(dict_foreign_err_buf);
((dict_index_t*)trx->error_info)->table_name, sprintf(ebuf + strlen(ebuf),
((dict_index_t*)trx->error_info)->name); " Error in foreign key constraint creation for table %.500s.\n"
"A foreign key constraint of name %.500s\n"
"already exists (note that internally InnoDB adds 'databasename/'\n"
"in front of the user-defined constraint name).\n", table->name, foreign->id);
fprintf(stderr, "%s\n", buf); ut_a(strlen(ebuf) < DICT_FOREIGN_ERR_BUF_LEN);
fprintf(stderr, mutex_exit(&dict_foreign_err_mutex);
"InnoDB: Maybe the internal data dictionary of InnoDB is\n"
"InnoDB: out-of-sync from the .frm files of your tables.\n" return(error);
"InnoDB: See section 15.1 Troubleshooting data dictionary operations\n"
"InnoDB: at http://www.innodb.com/ibman.html\n");
} }
if (error != DB_SUCCESS) {
fprintf(stderr,
"InnoDB: Foreign key constraint creation failed:\n"
"InnoDB: internal error number %lu\n", error);
mutex_enter(&dict_foreign_err_mutex); mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf); ut_sprintf_timestamp(ebuf);
sprintf(buf + strlen(buf), sprintf(ebuf + strlen(ebuf),
" Internal error in foreign key constraint creation for table %.500s.\n" " Internal error in foreign key constraint creation for table %.500s.\n"
"See the MySQL .err log in the datadir for more information.\n", table->name); "See the MySQL .err log in the datadir for more information.\n", table->name);
mutex_exit(&dict_foreign_err_mutex); mutex_exit(&dict_foreign_err_mutex);
......
...@@ -236,6 +236,29 @@ dict_remove_db_name( ...@@ -236,6 +236,29 @@ dict_remove_db_name(
return(NULL); return(NULL);
} }
/************************************************************************
Get the database name length in a table name. */
ulint
dict_get_db_name_len(
/*=================*/
/* out: database name length */
char* name) /* in: table name in the form dbname '/' tablename */
{
ulint i;
for (i = 0; i < 100000 ; i++) {
if (name[i] == '/') {
return(i);
}
}
ut_a(0);
return(0);
}
/************************************************************************ /************************************************************************
Reserves the dictionary system mutex for MySQL. */ Reserves the dictionary system mutex for MySQL. */
...@@ -869,6 +892,7 @@ dict_table_rename_in_cache( ...@@ -869,6 +892,7 @@ dict_table_rename_in_cache(
ulint fold; ulint fold;
ulint old_size; ulint old_size;
char* name_buf; char* name_buf;
char* old_name;
ulint i; ulint i;
ut_ad(table); ut_ad(table);
...@@ -899,6 +923,9 @@ dict_table_rename_in_cache( ...@@ -899,6 +923,9 @@ dict_table_rename_in_cache(
/* Remove table from the hash tables of tables */ /* Remove table from the hash tables of tables */
HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash,
ut_fold_string(table->name), table); ut_fold_string(table->name), table);
old_name = mem_heap_alloc(table->heap, ut_strlen(table->name) + 1);
ut_strcpy(old_name, table->name);
name_buf = mem_heap_alloc(table->heap, ut_strlen(new_name) + 1); name_buf = mem_heap_alloc(table->heap, ut_strlen(new_name) + 1);
...@@ -956,7 +983,9 @@ dict_table_rename_in_cache( ...@@ -956,7 +983,9 @@ dict_table_rename_in_cache(
return(TRUE); return(TRUE);
} }
/* Update the table name fields in foreign constraints */ /* Update the table name fields in foreign constraints, and update also
the constraint id of new format >= 4.0.18 constraints. Note that at
this point we have already changed table->name to the new name. */
foreign = UT_LIST_GET_FIRST(table->foreign_list); foreign = UT_LIST_GET_FIRST(table->foreign_list);
...@@ -965,14 +994,68 @@ dict_table_rename_in_cache( ...@@ -965,14 +994,68 @@ dict_table_rename_in_cache(
ut_strlen(table->name)) { ut_strlen(table->name)) {
/* Allocate a longer name buffer; /* Allocate a longer name buffer;
TODO: store buf len to save memory */ TODO: store buf len to save memory */
foreign->foreign_table_name = mem_heap_alloc( foreign->foreign_table_name = mem_heap_alloc(
foreign->heap, foreign->heap,
ut_strlen(table->name) + 1); ut_strlen(table->name) + 1);
} }
ut_memcpy(foreign->foreign_table_name, table->name, sprintf(foreign->foreign_table_name, "%s", table->name);
ut_strlen(table->name) + 1);
foreign->foreign_table_name[ut_strlen(table->name)] = '\0'; if (ut_str_contains(foreign->id, '/')) {
ulint db_len;
char old_id[2000];
/* This is a >= 4.0.18 format id */
ut_a(ut_strlen(foreign->id) < 1999);
ut_strcpy(old_id, foreign->id);
if (ut_strlen(foreign->id) > ut_strlen(old_name)
+ ut_strlen("_ibfk_")
&& 0 == ut_memcmp(foreign->id, old_name,
ut_strlen(old_name))
&& 0 == ut_memcmp(
foreign->id + ut_strlen(old_name),
(char*)"_ibfk_", ut_strlen("_ibfk_"))) {
/* This is a generated >= 4.0.18 format id */
if (ut_strlen(table->name)
> ut_strlen(old_name)) {
foreign->id = mem_heap_alloc(
foreign->heap,
ut_strlen(table->name)
+ ut_strlen(old_id) + 1);
}
/* Replace the prefix 'databasename/tablename'
with the new names */
sprintf(foreign->id, "%s%s", table->name,
old_id + ut_strlen(old_name));
} else {
/* This is a >= 4.0.18 format id where the user
gave the id name */
db_len = dict_get_db_name_len(table->name) + 1;
if (dict_get_db_name_len(table->name)
> dict_get_db_name_len(foreign->id)) {
foreign->id = mem_heap_alloc(
foreign->heap,
db_len + ut_strlen(old_id) + 1);
}
/* Replace the database prefix in id with the
one from table->name */
ut_memcpy(foreign->id, table->name, db_len);
sprintf(foreign->id + db_len, "%s",
dict_remove_db_name(old_id));
}
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
} }
...@@ -984,14 +1067,13 @@ dict_table_rename_in_cache( ...@@ -984,14 +1067,13 @@ dict_table_rename_in_cache(
ut_strlen(table->name)) { ut_strlen(table->name)) {
/* Allocate a longer name buffer; /* Allocate a longer name buffer;
TODO: store buf len to save memory */ TODO: store buf len to save memory */
foreign->referenced_table_name = mem_heap_alloc( foreign->referenced_table_name = mem_heap_alloc(
foreign->heap, foreign->heap,
ut_strlen(table->name) + 1); ut_strlen(table->name) + 1);
} }
ut_memcpy(foreign->referenced_table_name, table->name, sprintf(foreign->referenced_table_name, "%s", table->name);
ut_strlen(table->name) + 1);
foreign->referenced_table_name[ut_strlen(table->name)] = '\0';
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
} }
...@@ -2042,7 +2124,7 @@ static ...@@ -2042,7 +2124,7 @@ static
char* char*
dict_scan_to( dict_scan_to(
/*=========*/ /*=========*/
/* out: scanned up to this */
char* ptr, /* in: scan from */ char* ptr, /* in: scan from */
const char *string) /* in: look for this */ const char *string) /* in: look for this */
{ {
...@@ -2299,12 +2381,7 @@ dict_scan_table_name( ...@@ -2299,12 +2381,7 @@ dict_scan_table_name(
database_name = name; database_name = name;
i = 0; database_name_len = dict_get_db_name_len(name);
while (name[i] != '/') {
i++;
}
database_name_len = i;
} }
if (table_name_len + database_name_len > 2000) { if (table_name_len + database_name_len > 2000) {
...@@ -2478,6 +2555,52 @@ scan_more: ...@@ -2478,6 +2555,52 @@ scan_more:
} }
} }
/*************************************************************************
Finds the highest <number> for foreign key constraints of the table. Looks
only at the >= 4.0.18-format id's, which are of the form
databasename/tablename_ibfk_<number>. */
static
ulint
dict_table_get_highest_foreign_id(
/*==============================*/
/* out: highest number, 0 if table has no new
format foreign key constraints */
dict_table_t* table) /* in: table in the dictionary memory cache */
{
dict_foreign_t* foreign;
char* endp;
ulint biggest_id = 0;
ulint id;
ut_a(table);
foreign = UT_LIST_GET_FIRST(table->foreign_list);
while (foreign) {
if (ut_strlen(foreign->id) > ut_strlen("_ibfk_")
+ ut_strlen(table->name)
&& 0 == ut_memcmp(foreign->id, table->name,
ut_strlen(table->name))
&& 0 == ut_memcmp(foreign->id + ut_strlen(table->name),
(char*)"_ibfk_", ut_strlen("_ibfk_"))) {
/* It is of the >= 4.0.18 format */
id = strtoul(foreign->id + ut_strlen(table->name)
+ ut_strlen("_ibfk_"),
&endp, 10);
ut_a(id != biggest_id);
if (id > biggest_id) {
biggest_id = id;
}
}
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
}
return(biggest_id);
}
/************************************************************************* /*************************************************************************
Reports a simple foreign key create clause syntax error. */ Reports a simple foreign key create clause syntax error. */
static static
...@@ -2520,19 +2643,26 @@ dict_create_foreign_constraints_low( ...@@ -2520,19 +2643,26 @@ dict_create_foreign_constraints_low(
FOREIGN KEY (a, b) REFERENCES table2(c, d), FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database table2 can be written also with the database
name before it: test.table2; the default name before it: test.table2; the default
database id the database of parameter name */ database is the database of parameter name */
char* name) /* in: table full name in the normalized form char* name) /* in: table full name in the normalized form
database_name/table_name */ database_name/table_name */
{ {
dict_table_t* table; dict_table_t* table;
dict_table_t* referenced_table; dict_table_t* referenced_table;
dict_table_t* table_to_alter;
ulint highest_id_so_far = 0;
dict_index_t* index; dict_index_t* index;
dict_foreign_t* foreign; dict_foreign_t* foreign;
char* ptr = sql_string; char* ptr = sql_string;
char* start_of_latest_foreign = sql_string; char* start_of_latest_foreign = sql_string;
char* buf = dict_foreign_err_buf; char* buf = dict_foreign_err_buf;
char* constraint_name; /* this is NOT a null-
terminated string */
ulint constraint_name_len;
ibool success; ibool success;
ulint error; ulint error;
char* ptr1;
char* ptr2;
ulint i; ulint i;
ulint j; ulint j;
ibool is_on_delete; ibool is_on_delete;
...@@ -2559,16 +2689,89 @@ dict_create_foreign_constraints_low( ...@@ -2559,16 +2689,89 @@ dict_create_foreign_constraints_low(
return(DB_ERROR); return(DB_ERROR);
} }
/* First check if we are actually doing an ALTER TABLE, and in that
case look for the table being altered */
ptr = dict_accept(ptr, (char*) "ALTER", &success);
if (!success) {
goto loop;
}
ptr = dict_accept(ptr, (char*) "TABLE", &success);
if (!success) {
goto loop;
}
/* We are doing an ALTER TABLE: scan the table name we are altering;
in the call below we use the buffer 'referenced_table_name' as a dummy
buffer */
ptr = dict_scan_table_name(ptr, &table_to_alter, name,
&success, referenced_table_name);
if (!success) {
fprintf(stderr,
"InnoDB: Error: could not find the table being ALTERED in:\n%s\n", sql_string);
return(DB_ERROR);
}
/* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the
format databasename/tablename_ibfk_<number>, where <number> is local
to the table; look for the highest <number> for table_to_alter, so
that we can assign to new constraints higher numbers. */
/* If we are altering a temporary table, the table name after ALTER
TABLE does not correspond to the internal table name, and
table_to_alter is NULL. TODO: should we fix this somehow? */
if (table_to_alter == NULL) {
highest_id_so_far = 0;
} else {
highest_id_so_far = dict_table_get_highest_foreign_id(
table_to_alter);
}
/* Scan for foreign key declarations in a loop */
loop: loop:
ptr = dict_scan_to(ptr, (char *) "FOREIGN"); /* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */
if (*ptr == '\0') { ptr1 = dict_scan_to(ptr, (char *) "CONSTRAINT");
ptr2 = dict_scan_to(ptr, (char *) "FOREIGN");
constraint_name = NULL;
if (ptr1 < ptr2) {
/* The user has specified a constraint name. Pick it so
that we can store 'databasename/constraintname' as the id of
the id of the constraint to system tables. */
ptr = ptr1;
ptr = dict_accept(ptr, (char *) "CONSTRAINT", &success);
ut_a(success);
if (!isspace(*ptr)) {
goto loop;
}
ptr = dict_scan_id(ptr, &constraint_name, &constraint_name_len,
FALSE);
} else {
ptr = ptr2;
}
if (*ptr == '\0') {
/**********************************************************/
/* The following call adds the foreign key constraints /* The following call adds the foreign key constraints
to the data dictionary system tables on disk */ to the data dictionary system tables on disk */
error = dict_create_add_foreigns_to_dictionary(table, trx); error = dict_create_add_foreigns_to_dictionary(
highest_id_so_far, table, trx);
return(error); return(error);
} }
...@@ -2676,6 +2879,28 @@ col_loop1: ...@@ -2676,6 +2879,28 @@ col_loop1:
foreign = dict_mem_foreign_create(); foreign = dict_mem_foreign_create();
if (constraint_name) {
ulint db_len;
/* Catenate 'databasename/' to the constraint name specified
by the user: we conceive the constraint as belonging to the
same MySQL 'database' as the table itself. We store the name
to foreign->id. */
db_len = dict_get_db_name_len(table->name);
foreign->id = mem_heap_alloc(foreign->heap,
db_len + 1 + constraint_name_len + 1);
ut_memcpy(foreign->id, table->name, db_len);
foreign->id[db_len] = '/';
ut_memcpy(foreign->id + db_len + 1, constraint_name,
constraint_name_len);
foreign->id[db_len + 1 + constraint_name_len] = '\0';
}
foreign->foreign_table = table; foreign->foreign_table = table;
foreign->foreign_table_name = table->name; foreign->foreign_table_name = table->name;
foreign->foreign_index = index; foreign->foreign_index = index;
...@@ -2977,7 +3202,7 @@ dict_create_foreign_constraints( ...@@ -2977,7 +3202,7 @@ dict_create_foreign_constraints(
FOREIGN KEY (a, b) REFERENCES table2(c, d), FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database table2 can be written also with the database
name before it: test.table2; the default name before it: test.table2; the default
database id the database of parameter name */ database is the database of parameter name */
char* name) /* in: table full name in the normalized form char* name) /* in: table full name in the normalized form
database_name/table_name */ database_name/table_name */
{ {
...@@ -3079,8 +3304,10 @@ loop: ...@@ -3079,8 +3304,10 @@ loop:
foreign = UT_LIST_GET_FIRST(table->foreign_list); foreign = UT_LIST_GET_FIRST(table->foreign_list);
while (foreign != NULL) { while (foreign != NULL) {
if (0 == ut_strcmp(foreign->id, id)) { if (0 == ut_strcmp(foreign->id, id)
|| (ut_str_contains(foreign->id, '/')
&& 0 == ut_strcmp(id,
dict_remove_db_name(foreign->id)))) {
/* Found */ /* Found */
break; break;
} }
...@@ -3093,8 +3320,8 @@ loop: ...@@ -3093,8 +3320,8 @@ loop:
ut_sprintf_timestamp(buf); ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf), sprintf(buf + strlen(buf),
" Error in dropping of a foreign key constraint of table %.500s,\n" " Error in dropping of a foreign key constraint of table %.500s,\n"
"just before:\n%s\n in SQL command\n%s\nCannot find a constraint with the\n" "in SQL command\n%s\nCannot find a constraint with the\n"
"given id %s.\n", table->name, ptr, str, id); "given id %s.\n", table->name, str, id);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex); mutex_exit(&dict_foreign_err_mutex);
...@@ -3896,11 +4123,20 @@ dict_print_info_on_foreign_key_in_create_format( ...@@ -3896,11 +4123,20 @@ dict_print_info_on_foreign_key_in_create_format(
char* buf) /* in: buffer of at least 5000 bytes */ char* buf) /* in: buffer of at least 5000 bytes */
{ {
char* buf2 = buf; char* buf2 = buf;
char* stripped_id;
ulint cpy_len; ulint cpy_len;
ulint i; ulint i;
if (ut_str_contains(foreign->id, '/')) {
/* Strip the preceding database name from the constraint id */
stripped_id = foreign->id + 1
+ dict_get_db_name_len(foreign->id);
} else {
stripped_id = foreign->id;
}
buf2 += sprintf(buf2, ",\n CONSTRAINT `%s` FOREIGN KEY (", buf2 += sprintf(buf2, ",\n CONSTRAINT `%s` FOREIGN KEY (",
foreign->id); stripped_id);
for (i = 0; i < foreign->n_fields; i++) { for (i = 0; i < foreign->n_fields; i++) {
if ((ulint)(buf2 - buf) >= 4000) { if ((ulint)(buf2 - buf) >= 4000) {
......
...@@ -81,12 +81,25 @@ dict_create_or_check_foreign_constraint_tables(void); ...@@ -81,12 +81,25 @@ dict_create_or_check_foreign_constraint_tables(void);
/*================================================*/ /*================================================*/
/* out: DB_SUCCESS or error code */ /* out: DB_SUCCESS or error code */
/************************************************************************ /************************************************************************
Adds foreign key definitions to data dictionary tables in the database. */ Adds foreign key definitions to data dictionary tables in the database. We
look at table->foreign_list, and also generate names to constraints that were
not named by the user. A generated constraint has a name of the format
databasename/tablename_ibfk_<number>, where the numbers start from 1, and are
given locally for this table, that is, the number is not global, as in the
old format constraints < 4.0.18 it used to be. */
ulint ulint
dict_create_add_foreigns_to_dictionary( dict_create_add_foreigns_to_dictionary(
/*===================================*/ /*===================================*/
/* out: error code or DB_SUCCESS */ /* out: error code or DB_SUCCESS */
ulint start_id,/* in: if we are actually doing ALTER TABLE
ADD CONSTRAINT, we want to generate constraint
numbers which are bigger than in the table so
far; we number the constraints from
start_id + 1 up; start_id should be set to 0 if
we are creating a new table, or if the table
so far has no constraints for which the name
was generated here */
dict_table_t* table, /* in: table */ dict_table_t* table, /* in: table */
trx_t* trx); /* in: transaction */ trx_t* trx); /* in: transaction */
......
...@@ -26,6 +26,14 @@ Created 1/8/1996 Heikki Tuuri ...@@ -26,6 +26,14 @@ Created 1/8/1996 Heikki Tuuri
#include "ut0byte.h" #include "ut0byte.h"
#include "trx0types.h" #include "trx0types.h"
/************************************************************************
Get the database name length in a table name. */
ulint
dict_get_db_name_len(
/*=================*/
/* out: database name length */
char* name); /* in: table name in the form dbname '/' tablename */
/************************************************************************* /*************************************************************************
Accepts a specified string. Comparisons are case-insensitive. */ Accepts a specified string. Comparisons are case-insensitive. */
......
...@@ -85,10 +85,17 @@ ut_str_catenate( ...@@ -85,10 +85,17 @@ ut_str_catenate(
/* out, own: catenated null-terminated string */ /* out, own: catenated null-terminated string */
char* str1, /* in: null-terminated string */ char* str1, /* in: null-terminated string */
char* str2); /* in: null-terminated string */ char* str2); /* in: null-terminated string */
/**************************************************************************
Checks if a null-terminated string contains a certain character. */
ibool
ut_str_contains(
/*============*/
char* str, /* in: null-terminated string */
char c); /* in: character */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "ut0mem.ic" #include "ut0mem.ic"
#endif #endif
#endif #endif
...@@ -2221,6 +2221,7 @@ row_rename_table_for_mysql( ...@@ -2221,6 +2221,7 @@ row_rename_table_for_mysql(
ulint keywordlen; ulint keywordlen;
ulint len; ulint len;
ulint i; ulint i;
char* db_name;
char buf[10000]; char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
...@@ -2285,6 +2286,15 @@ row_rename_table_for_mysql( ...@@ -2285,6 +2286,15 @@ row_rename_table_for_mysql(
"PROCEDURE RENAME_TABLE_PROC () IS\n" "PROCEDURE RENAME_TABLE_PROC () IS\n"
"new_table_name CHAR;\n" "new_table_name CHAR;\n"
"old_table_name CHAR;\n" "old_table_name CHAR;\n"
"gen_constr_prefix CHAR;\n"
"new_db_name CHAR;\n"
"foreign_id CHAR;\n"
"new_foreign_id CHAR;\n"
"old_db_name_len INT;\n"
"old_t_name_len INT;\n"
"new_db_name_len INT;\n"
"id_len INT;\n"
"found INT;\n"
"BEGIN\n" "BEGIN\n"
"new_table_name :='"; "new_table_name :='";
...@@ -2311,32 +2321,94 @@ row_rename_table_for_mysql( ...@@ -2311,32 +2321,94 @@ row_rename_table_for_mysql(
} }
str3 = mem_heap_alloc(heap, str3 = mem_heap_alloc(heap,
1000 + 500 * n_constraints_to_drop); 1000 + 1000 * n_constraints_to_drop);
*str3 = '\0'; *str3 = '\0';
sprintf(str3, sprintf(str3,
"';\n" "';\n"
"UPDATE SYS_TABLES SET NAME = new_table_name\n" "UPDATE SYS_TABLES SET NAME = new_table_name\n"
"WHERE NAME = old_table_name;\n"); "WHERE NAME = old_table_name;\n");
db_name = mem_heap_alloc(heap, 1 + dict_get_db_name_len(
old_name));
ut_memcpy(db_name, old_name, dict_get_db_name_len(old_name));
db_name[dict_get_db_name_len(old_name)] = '\0';
/* Internally, old format < 4.0.18 constraints have as the
constraint id <number>_<number>, while new format constraints
have <databasename>/<constraintname>. */
for (i = 0; i < n_constraints_to_drop; i++) { for (i = 0; i < n_constraints_to_drop; i++) {
sprintf(str3 + strlen(str3),
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s/%s';\n"
"DELETE FROM SYS_FOREIGN WHERE ID = '%s/%s';\n",
db_name, constraints_to_drop[i],
db_name, constraints_to_drop[i]);
if (!ut_str_contains(constraints_to_drop[i], '/')) {
/* If this happens to be an old format
constraint, let us delete it. Since all new
format constraints contain '/', it does no
harm to run these DELETEs anyway. */
sprintf(str3 + strlen(str3), sprintf(str3 + strlen(str3),
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n" "DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n"
"DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n", "DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n",
constraints_to_drop[i], constraints_to_drop[i],
constraints_to_drop[i]); constraints_to_drop[i]);
} }
}
sprintf(str3 + strlen(str3), sprintf(str3 + strlen(str3),
"END;\n"); "END;\n");
ut_a(strlen(str3) < 1000 + 500 * n_constraints_to_drop); ut_a(strlen(str3) < 1000 + 1000 * n_constraints_to_drop);
} else { } else {
str3 = (char*) str3 = (char*)
"';\n" "';\n"
"UPDATE SYS_TABLES SET NAME = new_table_name\n" "UPDATE SYS_TABLES SET NAME = new_table_name\n"
"WHERE NAME = old_table_name;\n" "WHERE NAME = old_table_name;\n"
"UPDATE SYS_FOREIGN SET FOR_NAME = new_table_name\n" "found := 1;\n"
"WHERE FOR_NAME = old_table_name;\n" "old_db_name_len := INSTR(old_table_name, '/') - 1;\n"
"new_db_name_len := INSTR(new_table_name, '/') - 1;\n"
"new_db_name := SUBSTR(new_table_name, 0, new_db_name_len);\n"
"old_t_name_len := LENGTH(old_table_name);\n"
"gen_constr_prefix := CONCAT(old_table_name, '_ibfk_');\n"
"WHILE found = 1 LOOP\n"
" SELECT ID INTO foreign_id\n"
" FROM SYS_FOREIGN\n"
" WHERE FOR_NAME = old_table_name;\n"
" IF (SQL % NOTFOUND) THEN\n"
" found := 0;\n"
" ELSE\n"
" UPDATE SYS_FOREIGN\n"
" SET FOR_NAME = new_table_name\n"
" WHERE ID = foreign_id;\n"
" id_len := LENGTH(foreign_id);\n"
" IF (INSTR(foreign_id, '/') > 0) THEN\n"
" IF (INSTR(foreign_id,\n"
" gen_constr_prefix) > 0)\n"
" THEN\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"
" ELSE\n"
" new_foreign_id :=\n"
" CONCAT(new_db_name,\n"
" SUBSTR(foreign_id,\n"
" old_db_name_len,\n"
" id_len - old_db_name_len));\n"
" END IF;\n"
" UPDATE SYS_FOREIGN\n"
" SET ID = new_foreign_id\n"
" WHERE ID = foreign_id;\n"
" UPDATE SYS_FOREIGN_COLS\n"
" SET ID = new_foreign_id\n"
" WHERE ID = foreign_id;\n"
" END IF;\n"
" END IF;\n"
"END LOOP;\n"
"UPDATE SYS_FOREIGN SET REF_NAME = new_table_name\n" "UPDATE SYS_FOREIGN SET REF_NAME = new_table_name\n"
"WHERE REF_NAME = old_table_name;\n" "WHERE REF_NAME = old_table_name;\n"
"END;\n"; "END;\n";
......
...@@ -159,7 +159,7 @@ struct sync_thread_struct{ ...@@ -159,7 +159,7 @@ struct sync_thread_struct{
}; };
/* Number of slots reserved for each OS thread in the sync level array */ /* Number of slots reserved for each OS thread in the sync level array */
#define SYNC_THREAD_N_LEVELS 250 #define SYNC_THREAD_N_LEVELS 10000
struct sync_level_struct{ struct sync_level_struct{
void* latch; /* pointer to a mutex or an rw-lock; NULL means that void* latch; /* pointer to a mutex or an rw-lock; NULL means that
......
...@@ -221,3 +221,27 @@ ut_str_catenate( ...@@ -221,3 +221,27 @@ ut_str_catenate(
return(str); return(str);
} }
/**************************************************************************
Checks if a null-terminated string contains a certain character. */
ibool
ut_str_contains(
/*============*/
char* str, /* in: null-terminated string */
char c) /* in: character */
{
ulint len;
ulint i;
len = ut_strlen(str);
for (i = 0; i < len; i++) {
if (str[i] == c) {
return(TRUE);
}
}
return(FALSE);
}
\ No newline at end of file
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