summaryrefslogtreecommitdiff
path: root/innobase/dict
diff options
context:
space:
mode:
authorunknown <heikki@hundin.mysql.fi>2004-02-09 01:32:00 +0200
committerunknown <heikki@hundin.mysql.fi>2004-02-09 01:32:00 +0200
commitcd361b41ac673e6d8fb190a87a75419bb7a2317b (patch)
tree97777e82a25d2f9e6639039e005ecfe3303abd56 /innobase/dict
parentb0076aad731e2e2ea12c5261e0b89c27c5e34f2f (diff)
downloadmariadb-git-cd361b41ac673e6d8fb190a87a75419bb7a2317b.tar.gz
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 innobase/dict/dict0crea.c: 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 innobase/dict/dict0dict.c: 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 innobase/include/dict0crea.h: 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 innobase/include/dict0dict.h: 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 innobase/include/ut0mem.h: 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 innobase/row/row0mysql.c: 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 innobase/sync/sync0sync.c: UNIV_SYNC_DEBUG caused assertion in the creation of the doublewrite buffer, if we do not allow thousands of latches per thread innobase/ut/ut0mem.c: 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
Diffstat (limited to 'innobase/dict')
-rw-r--r--innobase/dict/dict0crea.c79
-rw-r--r--innobase/dict/dict0dict.c286
2 files changed, 307 insertions, 58 deletions
diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c
index 9139e589a0a..0445c339fe9 100644
--- a/innobase/dict/dict0crea.c
+++ b/innobase/dict/dict0crea.c
@@ -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 @@ loop:
"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 @@ loop:
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 @@ loop:
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);
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c
index 87505656d5a..e0597c66dba 100644
--- a/innobase/dict/dict0dict.c
+++ b/innobase/dict/dict0dict.c
@@ -235,6 +235,29 @@ dict_remove_db_name(
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. */
@@ -869,6 +892,7 @@ dict_table_rename_in_cache(
ulint fold;
ulint old_size;
char* name_buf;
+ char* old_name;
ulint i;
ut_ad(table);
@@ -899,6 +923,9 @@ dict_table_rename_in_cache(
/* Remove table from the hash tables of tables */
HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash,
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);
@@ -956,7 +983,9 @@ dict_table_rename_in_cache(
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);
@@ -965,14 +994,68 @@ dict_table_rename_in_cache(
ut_strlen(table->name)) {
/* Allocate a longer name buffer;
TODO: store buf len to save memory */
+
foreign->foreign_table_name = mem_heap_alloc(
foreign->heap,
ut_strlen(table->name) + 1);
}
- ut_memcpy(foreign->foreign_table_name, table->name,
- ut_strlen(table->name) + 1);
- foreign->foreign_table_name[ut_strlen(table->name)] = '\0';
+ sprintf(foreign->foreign_table_name, "%s", table->name);
+
+ 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);
}
@@ -984,14 +1067,13 @@ dict_table_rename_in_cache(
ut_strlen(table->name)) {
/* Allocate a longer name buffer;
TODO: store buf len to save memory */
+
foreign->referenced_table_name = mem_heap_alloc(
foreign->heap,
ut_strlen(table->name) + 1);
}
- ut_memcpy(foreign->referenced_table_name, table->name,
- ut_strlen(table->name) + 1);
- foreign->referenced_table_name[ut_strlen(table->name)] = '\0';
+ sprintf(foreign->referenced_table_name, "%s", table->name);
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
}
@@ -2042,7 +2124,7 @@ static
char*
dict_scan_to(
/*=========*/
-
+ /* out: scanned up to this */
char* ptr, /* in: scan from */
const char *string) /* in: look for this */
{
@@ -2299,12 +2381,7 @@ dict_scan_table_name(
database_name = name;
- i = 0;
- while (name[i] != '/') {
- i++;
- }
-
- database_name_len = i;
+ database_name_len = dict_get_db_name_len(name);
}
if (table_name_len + database_name_len > 2000) {
@@ -2479,6 +2556,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. */
static
void
@@ -2520,19 +2643,26 @@ dict_create_foreign_constraints_low(
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database
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
database_name/table_name */
{
dict_table_t* table;
dict_table_t* referenced_table;
+ dict_table_t* table_to_alter;
+ ulint highest_id_so_far = 0;
dict_index_t* index;
dict_foreign_t* foreign;
char* ptr = sql_string;
char* start_of_latest_foreign = sql_string;
char* buf = dict_foreign_err_buf;
+ char* constraint_name; /* this is NOT a null-
+ terminated string */
+ ulint constraint_name_len;
ibool success;
ulint error;
+ char* ptr1;
+ char* ptr2;
ulint i;
ulint j;
ibool is_on_delete;
@@ -2559,16 +2689,89 @@ dict_create_foreign_constraints_low(
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:
- 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
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);
}
@@ -2676,6 +2879,28 @@ col_loop1:
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_name = table->name;
foreign->foreign_index = index;
@@ -2977,7 +3202,7 @@ dict_create_foreign_constraints(
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database
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
database_name/table_name */
{
@@ -3079,8 +3304,10 @@ loop:
foreign = UT_LIST_GET_FIRST(table->foreign_list);
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 */
break;
}
@@ -3093,8 +3320,8 @@ loop:
ut_sprintf_timestamp(buf);
sprintf(buf + strlen(buf),
" 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"
-"given id %s.\n", table->name, ptr, str, id);
+"in SQL command\n%s\nCannot find a constraint with the\n"
+"given id %s.\n", table->name, str, id);
ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
mutex_exit(&dict_foreign_err_mutex);
@@ -3896,11 +4123,20 @@ dict_print_info_on_foreign_key_in_create_format(
char* buf) /* in: buffer of at least 5000 bytes */
{
char* buf2 = buf;
+ char* stripped_id;
ulint cpy_len;
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 (",
- foreign->id);
+ stripped_id);
for (i = 0; i < foreign->n_fields; i++) {
if ((ulint)(buf2 - buf) >= 4000) {