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)
}
/************************************************************************
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
dict_create_add_foreigns_to_dictionary(
/*===================================*/
/* 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 */
trx_t* trx) /* in: transaction */
{
dict_foreign_t* foreign;
que_thr_t* thr;
que_t* graph;
dulint id;
ulint number = start_id + 1;
ulint len;
ulint error;
char* ebuf = dict_foreign_err_buf;
ulint i;
char buf2[50];
char buf[10000];
ut_ad(mutex_own(&(dict_sys->mutex)));
......@@ -1215,18 +1228,18 @@ dict_create_add_foreigns_to_dictionary(
"PROCEDURE ADD_FOREIGN_DEFS_PROC () IS\n"
"BEGIN\n");
/* We allocate the new id from the sequence of table id's */
id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
if (foreign->id == NULL) {
/* Generate a new constraint id */
foreign->id = mem_heap_alloc(foreign->heap,
ut_strlen(table->name)
+ 20);
sprintf(foreign->id, "%s_ibfk_%lu", table->name, number);
number++;
}
sprintf(buf2, "%lu_%lu", ut_dulint_get_high(id),
ut_dulint_get_low(id));
foreign->id = mem_heap_alloc(foreign->heap, ut_strlen(buf2) + 1);
ut_memcpy(foreign->id, buf2, ut_strlen(buf2) + 1);
len += sprintf(buf + len,
"INSERT INTO SYS_FOREIGN VALUES('%lu_%lu', '%s', '%s', %lu);\n",
ut_dulint_get_high(id),
ut_dulint_get_low(id),
"INSERT INTO SYS_FOREIGN VALUES('%s', '%s', '%s', %lu);\n",
foreign->id,
table->name,
foreign->referenced_table_name,
foreign->n_fields
......@@ -1235,9 +1248,8 @@ dict_create_add_foreigns_to_dictionary(
for (i = 0; i < foreign->n_fields; i++) {
len += sprintf(buf + len,
"INSERT INTO SYS_FOREIGN_COLS VALUES('%lu_%lu', %lu, '%s', '%s');\n",
ut_dulint_get_high(id),
ut_dulint_get_low(id),
"INSERT INTO SYS_FOREIGN_COLS VALUES('%s', %lu, '%s', '%s');\n",
foreign->id,
i,
foreign->foreign_col_names[i],
foreign->referenced_col_names[i]);
......@@ -1262,29 +1274,30 @@ dict_create_add_foreigns_to_dictionary(
que_graph_free(graph);
if (error == DB_DUPLICATE_KEY) {
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(dict_foreign_err_buf);
sprintf(ebuf + strlen(ebuf),
" 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);
ut_a(strlen(ebuf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
return(error);
}
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) {
fprintf(stderr,
"InnoDB: Duplicate key error in system table %s index %s\n",
((dict_index_t*)trx->error_info)->table_name,
((dict_index_t*)trx->error_info)->name);
fprintf(stderr, "%s\n", buf);
fprintf(stderr,
"InnoDB: Maybe the internal data dictionary of InnoDB is\n"
"InnoDB: out-of-sync from the .frm files of your tables.\n"
"InnoDB: See section 15.1 Troubleshooting data dictionary operations\n"
"InnoDB: at http://www.innodb.com/ibman.html\n");
}
mutex_enter(&dict_foreign_err_mutex);
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
ut_sprintf_timestamp(ebuf);
sprintf(ebuf + strlen(ebuf),
" 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);
mutex_exit(&dict_foreign_err_mutex);
......
This diff is collapsed.
......@@ -81,12 +81,25 @@ dict_create_or_check_foreign_constraint_tables(void);
/*================================================*/
/* 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
dict_create_add_foreigns_to_dictionary(
/*===================================*/
/* 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 */
trx_t* trx); /* in: transaction */
......
......@@ -26,6 +26,14 @@ Created 1/8/1996 Heikki Tuuri
#include "ut0byte.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. */
......
......@@ -85,10 +85,17 @@ ut_str_catenate(
/* out, own: catenated null-terminated string */
char* str1, /* 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
#include "ut0mem.ic"
#endif
#endif
......@@ -2221,6 +2221,7 @@ row_rename_table_for_mysql(
ulint keywordlen;
ulint len;
ulint i;
char* db_name;
char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
......@@ -2285,6 +2286,15 @@ row_rename_table_for_mysql(
"PROCEDURE RENAME_TABLE_PROC () IS\n"
"new_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"
"new_table_name :='";
......@@ -2311,32 +2321,94 @@ row_rename_table_for_mysql(
}
str3 = mem_heap_alloc(heap,
1000 + 500 * n_constraints_to_drop);
1000 + 1000 * n_constraints_to_drop);
*str3 = '\0';
sprintf(str3,
"';\n"
"UPDATE SYS_TABLES SET NAME = new_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++) {
sprintf(str3 + strlen(str3),
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n"
"DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n",
"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),
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n"
"DELETE FROM SYS_FOREIGN WHERE ID = '%s';\n",
constraints_to_drop[i],
constraints_to_drop[i]);
}
}
sprintf(str3 + strlen(str3),
"END;\n");
ut_a(strlen(str3) < 1000 + 500 * n_constraints_to_drop);
ut_a(strlen(str3) < 1000 + 1000 * n_constraints_to_drop);
} else {
str3 = (char*)
"';\n"
"UPDATE SYS_TABLES SET NAME = new_table_name\n"
"WHERE NAME = old_table_name;\n"
"UPDATE SYS_FOREIGN SET FOR_NAME = new_table_name\n"
"WHERE FOR_NAME = old_table_name;\n"
"found := 1;\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"
"WHERE REF_NAME = old_table_name;\n"
"END;\n";
......
......@@ -159,7 +159,7 @@ struct sync_thread_struct{
};
/* 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{
void* latch; /* pointer to a mutex or an rw-lock; NULL means that
......
......@@ -221,3 +221,27 @@ ut_str_catenate(
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