mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
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
This commit is contained in:
@@ -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:
|
|||||||
"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,
|
||||||
|
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,
|
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:
|
|||||||
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:
|
|||||||
|
|
||||||
que_graph_free(graph);
|
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) {
|
if (error != DB_SUCCESS) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"InnoDB: Foreign key constraint creation failed:\n"
|
"InnoDB: Foreign key constraint creation failed:\n"
|
||||||
"InnoDB: internal error number %lu\n", error);
|
"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);
|
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);
|
||||||
|
@@ -235,6 +235,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(
|
|||||||
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(
|
|||||||
/* 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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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
|
|||||||
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(
|
|||||||
|
|
||||||
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:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
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(
|
|||||||
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(
|
|||||||
|
|
||||||
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 */
|
||||||
|
|
||||||
|
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') {
|
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:
|
|||||||
|
|
||||||
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(
|
|||||||
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:
|
|||||||
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:
|
|||||||
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(
|
|||||||
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);
|
|||||||
/*================================================*/
|
/*================================================*/
|
||||||
/* 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
|
|||||||
#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(
|
|||||||
/* 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(
|
|||||||
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(
|
|||||||
"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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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),
|
sprintf(str3 + strlen(str3),
|
||||||
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s';\n"
|
"DELETE FROM SYS_FOREIGN_COLS WHERE ID = '%s/%s';\n"
|
||||||
"DELETE FROM SYS_FOREIGN WHERE ID = '%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],
|
||||||
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{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* 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(
|
|||||||
|
|
||||||
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);
|
||||||
|
}
|
Reference in New Issue
Block a user