summaryrefslogtreecommitdiff
path: root/innobase/row
diff options
context:
space:
mode:
authorunknown <heikki@hundin.mysql.fi>2003-04-16 16:45:01 +0300
committerunknown <heikki@hundin.mysql.fi>2003-04-16 16:45:01 +0300
commit33ac47279b7367c816c90642f1b4e530c05f248e (patch)
tree7fff68a7504903b0da22aa86bbf09e69351a2184 /innobase/row
parent07c29cc91aaba8a7982db9b7ba9c95ffc9ab7445 (diff)
downloadmariadb-git-33ac47279b7367c816c90642f1b4e530c05f248e.tar.gz
Many files:
Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/buf/buf0flu.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/dict/dict0crea.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/dict/dict0dict.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/dict/dict0load.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/ibuf/ibuf0ibuf.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/include/db0err.h: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/include/dict0dict.h: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/include/row0ins.h: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/lock/lock0lock.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/os/os0file.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0ins.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0mysql.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0sel.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/row/row0upd.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/srv/srv0srv.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works innobase/ut/ut0ut.c: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works sql/ha_innodb.cc: Merge InnoDB-4.0.13; DROP FOREIGN KEY now works
Diffstat (limited to 'innobase/row')
-rw-r--r--innobase/row/row0ins.c160
-rw-r--r--innobase/row/row0mysql.c121
-rw-r--r--innobase/row/row0sel.c35
-rw-r--r--innobase/row/row0upd.c2
4 files changed, 256 insertions, 62 deletions
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c
index 990ef99b2a4..1a616b74756 100644
--- a/innobase/row/row0ins.c
+++ b/innobase/row/row0ins.c
@@ -438,6 +438,111 @@ row_ins_cascade_calc_update_vec(
}
/*************************************************************************
+Reports a foreign key error associated with an update or a delete of a
+parent table index entry. */
+static
+void
+row_ins_foreign_report_err(
+/*=======================*/
+ char* errstr, /* in: error string from the viewpoint
+ of the parent table */
+ que_thr_t* thr, /* in: query thread whose run_node
+ is an update node */
+ dict_foreign_t* foreign, /* in: foreign key constraint */
+ rec_t* rec, /* in: a matching index record in the
+ child table */
+ dtuple_t* entry) /* in: index entry in the parent
+ table */
+{
+ char* buf = dict_foreign_err_buf;
+
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf), " Transaction:\n");
+ trx_print(buf + strlen(buf), thr_get_trx(thr));
+
+ sprintf(buf + strlen(buf),
+"Foreign key constraint fails for table %.500s:\n",
+ foreign->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ foreign, buf + strlen(buf));
+ sprintf(buf + strlen(buf), "\n%s", errstr);
+ sprintf(buf + strlen(buf),
+" in parent table, in index %.500s tuple:\n",
+ foreign->referenced_index->name);
+ if (entry) {
+ dtuple_sprintf(buf + strlen(buf), 1000, entry);
+ }
+ sprintf(buf + strlen(buf),
+"\nBut in child table %.500s, in index %.500s, there is a record:\n",
+ foreign->foreign_table_name, foreign->foreign_index->name);
+ if (rec) {
+ rec_sprintf(buf + strlen(buf), 1000, rec);
+ }
+ sprintf(buf + strlen(buf), "\n");
+
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+
+ mutex_exit(&dict_foreign_err_mutex);
+}
+
+/*************************************************************************
+Reports a foreign key error to dict_foreign_err_buf when we are trying
+to add an index entry to a child table. Note that the adding may be the result
+of an update, too. */
+static
+void
+row_ins_foreign_report_add_err(
+/*===========================*/
+ que_thr_t* thr, /* in: query thread whose run_node
+ is an insert node */
+ dict_foreign_t* foreign, /* in: foreign key constraint */
+ rec_t* rec, /* in: a record in the parent table:
+ it does not match entry because we
+ have an error! */
+ dtuple_t* entry) /* in: index entry to insert in the
+ child table */
+{
+ char* buf = dict_foreign_err_buf;
+
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf), " Transaction:\n");
+ trx_print(buf + strlen(buf), thr_get_trx(thr));
+ sprintf(buf + strlen(buf),
+"Foreign key constraint fails for table %.500s:\n",
+ foreign->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ foreign, buf + strlen(buf));
+ sprintf(buf + strlen(buf),
+"\nTrying to add in child table, in index %.500s tuple:\n",
+ foreign->foreign_index->name);
+ if (entry) {
+ dtuple_sprintf(buf + strlen(buf), 1000, entry);
+ }
+ sprintf(buf + strlen(buf),
+"\nBut in parent table %.500s, in index %.500s,\n"
+"the closest match we can find is record:\n",
+ foreign->referenced_table_name,
+ foreign->referenced_index->name);
+ if (rec && page_rec_is_supremum(rec)) {
+ /* If the cursor ended on a supremum record, it is better
+ to report the previous record in the error message, so that
+ the user gets a more descriptive error message. */
+ rec = page_rec_get_prev(rec);
+ }
+
+ if (rec) {
+ rec_sprintf(buf + strlen(buf), 1000, rec);
+ }
+ sprintf(buf + strlen(buf), "\n");
+
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+
+ mutex_exit(&dict_foreign_err_mutex);
+}
+
+/*************************************************************************
Perform referential actions or checks when a parent row is deleted or updated
and the constraint had an ON DELETE or ON UPDATE condition which was not
RESTRICT. */
@@ -453,6 +558,8 @@ row_ins_foreign_check_on_constraint(
type is != 0 */
btr_pcur_t* pcur, /* in: cursor placed on a matching
index record in the child table */
+ dtuple_t* entry, /* in: index entry in the parent
+ table */
mtr_t* mtr) /* in: mtr holding the latch of pcur
page */
{
@@ -506,6 +613,10 @@ row_ins_foreign_check_on_constraint(
return(DB_SUCCESS);
}
+ row_ins_foreign_report_err((char*)"Trying to delete",
+ thr, foreign,
+ btr_pcur_get_rec(pcur), entry);
+
return(DB_ROW_IS_REFERENCED);
}
@@ -523,6 +634,10 @@ row_ins_foreign_check_on_constraint(
return(DB_SUCCESS);
}
+ row_ins_foreign_report_err((char*)"Trying to update",
+ thr, foreign,
+ btr_pcur_get_rec(pcur), entry);
+
return(DB_ROW_IS_REFERENCED);
}
@@ -580,6 +695,10 @@ row_ins_foreign_check_on_constraint(
err = DB_ROW_IS_REFERENCED;
+ row_ins_foreign_report_err(
+(char*)"Trying an update, possibly causing a cyclic cascaded update\n"
+"in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry);
+
goto nonstandard_exit_func;
}
@@ -809,11 +928,10 @@ row_ins_check_foreign_constraint(
dictionary cache if they exist at all */
dict_table_t* table, /* in: if check_ref is TRUE, then the foreign
table, else the referenced table */
- dict_index_t* index __attribute__((unused)),/* in: index in table */
dtuple_t* entry, /* in: index entry for index */
que_thr_t* thr) /* in: query thread */
{
- upd_node_t* upd_node;
+ upd_node_t* upd_node;
dict_table_t* check_table;
dict_index_t* check_index;
ulint n_fields_cmp;
@@ -824,6 +942,7 @@ row_ins_check_foreign_constraint(
int cmp;
ulint err;
ulint i;
+ char* buf = dict_foreign_err_buf;
mtr_t mtr;
run_again:
@@ -884,6 +1003,25 @@ run_again:
if (check_table == NULL) {
if (check_ref) {
+ mutex_enter(&dict_foreign_err_mutex);
+ ut_sprintf_timestamp(buf);
+ sprintf(buf + strlen(buf), " Transaction:\n");
+ trx_print(buf + strlen(buf), thr_get_trx(thr));
+ sprintf(buf + strlen(buf),
+"Foreign key constraint fails for table %.500s:\n",
+ foreign->foreign_table_name);
+ dict_print_info_on_foreign_key_in_create_format(
+ foreign, buf + strlen(buf));
+ sprintf(buf + strlen(buf),
+"\nTrying to add to index %.500s tuple:\n", foreign->foreign_index->name);
+ dtuple_sprintf(buf + strlen(buf), 1000, entry);
+ sprintf(buf + strlen(buf),
+"\nBut the parent table %.500s does not currently exist!\n",
+ foreign->referenced_table_name);
+
+ ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN);
+ mutex_exit(&dict_foreign_err_mutex);
+
return(DB_NO_REFERENCED_ROW);
}
@@ -949,7 +1087,8 @@ run_again:
if (cmp == 0) {
if (rec_get_deleted_flag(rec)) {
- err = row_ins_set_shared_rec_lock(LOCK_ORDINARY,
+ err = row_ins_set_shared_rec_lock(
+ LOCK_ORDINARY,
rec, check_index, thr);
if (err != DB_SUCCESS) {
@@ -989,13 +1128,17 @@ run_again:
err =
row_ins_foreign_check_on_constraint(
- thr, foreign, &pcur, &mtr);
-
+ thr, foreign, &pcur, entry,
+ &mtr);
if (err != DB_SUCCESS) {
break;
}
} else {
+ row_ins_foreign_report_err(
+ (char*)"Trying to delete or update",
+ thr, foreign, rec, entry);
+
err = DB_ROW_IS_REFERENCED;
break;
}
@@ -1012,6 +1155,8 @@ run_again:
if (check_ref) {
err = DB_NO_REFERENCED_ROW;
+ row_ins_foreign_report_add_err(
+ thr, foreign, rec, entry);
} else {
err = DB_SUCCESS;
}
@@ -1025,6 +1170,9 @@ next_rec:
if (!moved) {
if (check_ref) {
+ rec = btr_pcur_get_rec(&pcur);
+ row_ins_foreign_report_add_err(
+ thr, foreign, rec, entry);
err = DB_NO_REFERENCED_ROW;
} else {
err = DB_SUCCESS;
@@ -1100,7 +1248,7 @@ row_ins_check_foreign_constraints(
}
err = row_ins_check_foreign_constraint(TRUE, foreign,
- table, index, entry, thr);
+ table, entry, thr);
if (got_s_lock) {
row_mysql_unfreeze_data_dictionary(trx);
}
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 0ca1a7516e0..81ed94e2628 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -6,7 +6,7 @@ Contains also create table and other data dictionary operations.
Created 9/17/2000 Heikki Tuuri
*******************************************************/
-
+
#include "row0mysql.h"
#ifdef UNIV_NONINL
@@ -1156,7 +1156,7 @@ row_mysql_recover_tmp_table(
return(DB_ERROR);
}
- if (0 == ut_memcmp(ptr, "/rsql", 5)) {
+ if (0 == ut_memcmp(ptr, (char*)"/rsql", 5)) {
ptr++;
*ptr = '#';
@@ -1280,10 +1280,10 @@ row_create_table_for_mysql(
}
trx->op_info = (char *) "creating table";
-
- if (0 == ut_strcmp(table->name, "mysql/host")
- || 0 == ut_strcmp(table->name, "mysql/user")
- || 0 == ut_strcmp(table->name, "mysql/db")) {
+
+ if (0 == ut_strcmp(table->name, (char*)"mysql/host")
+ || 0 == ut_strcmp(table->name, (char*)"mysql/user")
+ || 0 == ut_strcmp(table->name, (char*)"mysql/db")) {
fprintf(stderr,
"InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n"
@@ -1303,7 +1303,7 @@ row_create_table_for_mysql(
if (namelen >= keywordlen
&& 0 == ut_memcmp(table->name + namelen - keywordlen,
- "_recover_innodb_tmp_table", keywordlen)) {
+ (char*)"_recover_innodb_tmp_table", keywordlen)) {
/* MySQL prevents accessing of tables whose name begins
with #sql, that is temporary tables. If mysqld crashes in
@@ -1371,7 +1371,7 @@ row_create_table_for_mysql(
if (namelen >= keywordlen
&& 0 == ut_memcmp(table->name + namelen - keywordlen,
- "innodb_mem_validate", keywordlen)) {
+ (char*)"innodb_mem_validate", keywordlen)) {
/* We define here a debugging feature intended for
developers */
@@ -1481,7 +1481,7 @@ row_create_index_for_mysql(
if (namelen >= keywordlen
&& 0 == ut_memcmp(
index->table_name + namelen - keywordlen,
- "_recover_innodb_tmp_table", keywordlen)) {
+ (char*)"_recover_innodb_tmp_table", keywordlen)) {
return(DB_SUCCESS);
}
@@ -1586,7 +1586,7 @@ row_table_add_foreign_constraints(
if (namelen >= keywordlen
&& 0 == ut_memcmp(
name + namelen - keywordlen,
- "_recover_innodb_tmp_table", keywordlen)) {
+ (char*)"_recover_innodb_tmp_table", keywordlen)) {
return(DB_SUCCESS);
}
@@ -1808,7 +1808,6 @@ row_drop_table_for_mysql(
ulint len;
ulint namelen;
ulint keywordlen;
- ulint rounds = 0;
ibool locked_dictionary = FALSE;
char buf[10000];
@@ -2155,7 +2154,7 @@ row_is_mysql_tmp_table_name(
ulint i;
for (i = 0; i <= ut_strlen(name) - 5; i++) {
- if (ut_memcmp(name + i, "/#sql", 5) == 0) {
+ if (ut_memcmp(name + i, (char*)"/#sql", 5) == 0) {
return(TRUE);
}
@@ -2177,12 +2176,16 @@ row_rename_table_for_mysql(
{
dict_table_t* table;
que_thr_t* thr;
- que_t* graph;
+ que_t* graph = NULL;
ulint err;
char* str1;
char* str2;
char* str3;
+ mem_heap_t* heap = NULL;
+ char** constraints_to_drop = NULL;
+ ulint n_constraints_to_drop = 0;
ulint len;
+ ulint i;
char buf[10000];
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
@@ -2200,10 +2203,10 @@ row_rename_table_for_mysql(
trx_commit_for_mysql(trx);
return(DB_ERROR);
}
-
- if (0 == ut_strcmp(new_name, "mysql/host")
- || 0 == ut_strcmp(new_name, "mysql/user")
- || 0 == ut_strcmp(new_name, "mysql/db")) {
+
+ if (0 == ut_strcmp(new_name, (char*)"mysql/host")
+ || 0 == ut_strcmp(new_name, (char*)"mysql/user")
+ || 0 == ut_strcmp(new_name, (char*)"mysql/db")) {
fprintf(stderr,
"InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n"
@@ -2217,6 +2220,19 @@ row_rename_table_for_mysql(
trx->op_info = (char *) "renaming table";
trx_start_if_not_started(trx);
+ /* Serialize data dictionary operations with dictionary mutex:
+ no deadlocks can occur then in these operations */
+
+ row_mysql_lock_data_dictionary(trx);
+
+ table = dict_table_get_low(old_name);
+
+ if (!table) {
+ err = DB_TABLE_NOT_FOUND;
+
+ goto funct_exit;
+ }
+
str1 = (char *)
"PROCEDURE RENAME_TABLE_PROC () IS\n"
"new_table_name CHAR;\n"
@@ -2229,14 +2245,43 @@ row_rename_table_for_mysql(
if (row_is_mysql_tmp_table_name(new_name)) {
- /* We want to preserve the original foreign key
- constraint definitions despite the name change */
+ /* MySQL is doing an ALTER TABLE command and it renames the
+ original table to a temporary table name. We want to preserve
+ the original foreign key constraint definitions despite the
+ name change. An exception is those constraints for which
+ the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/
- str3 = (char*)
- "';\n"
- "UPDATE SYS_TABLES SET NAME = new_table_name\n"
- "WHERE NAME = old_table_name;\n"
- "END;\n";
+ heap = mem_heap_create(100);
+
+ err = dict_foreign_parse_drop_constraints(heap, trx,
+ table,
+ &n_constraints_to_drop,
+ &constraints_to_drop);
+ if (err != DB_SUCCESS) {
+
+ goto funct_exit;
+ }
+
+ str3 = mem_heap_alloc(heap,
+ 1000 + 500 * n_constraints_to_drop);
+ *str3 = '\0';
+ sprintf(str3,
+ "';\n"
+ "UPDATE SYS_TABLES SET NAME = new_table_name\n"
+ "WHERE NAME = old_table_name;\n");
+
+ 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",
+ constraints_to_drop[i],
+ constraints_to_drop[i]);
+ }
+
+ sprintf(str3 + strlen(str3),
+ "END;\n");
+
+ ut_a(strlen(str3) < 1000 + 500 * n_constraints_to_drop);
} else {
str3 = (char*)
"';\n"
@@ -2267,13 +2312,6 @@ row_rename_table_for_mysql(
ut_memcpy(buf + len, str3, ut_strlen(str3) + 1);
- /* Serialize data dictionary operations with dictionary mutex:
- no deadlocks can occur then in these operations */
-
- row_mysql_lock_data_dictionary(trx);
-
- table = dict_table_get_low(old_name);
-
graph = pars_sql(buf);
ut_a(graph);
@@ -2283,12 +2321,6 @@ row_rename_table_for_mysql(
graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
- if (!table) {
- err = DB_TABLE_NOT_FOUND;
-
- goto funct_exit;
- }
-
ut_a(thr = que_fork_start_command(graph, SESS_COMM_EXECUTE, 0));
que_run_threads(thr);
@@ -2329,6 +2361,13 @@ row_rename_table_for_mysql(
if (row_is_mysql_tmp_table_name(old_name)) {
+ /* MySQL is doing an ALTER TABLE command and it
+ renames the created temporary table to the name
+ of the original table. In the ALTER TABLE we maybe
+ created some FOREIGN KEY constraints for the temporary
+ table. But we want to load also the foreign key
+ constraint definitions for the original table name. */
+
err = dict_load_foreigns(new_name);
if (err != DB_SUCCESS) {
@@ -2354,7 +2393,13 @@ row_rename_table_for_mysql(
funct_exit:
row_mysql_unlock_data_dictionary(trx);
- que_graph_free(graph);
+ if (graph) {
+ que_graph_free(graph);
+ }
+
+ if (heap) {
+ mem_heap_free(heap);
+ }
trx_commit_for_mysql(trx);
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index 4bc81ad8a8e..40994c9962c 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2602,6 +2602,24 @@ row_search_for_mysql(
printf("N tables locked %lu\n", trx->mysql_n_tables_locked);
*/
/*-------------------------------------------------------------*/
+ /* PHASE 0: Release a possible s-latch we are holding on the
+ adaptive hash index latch if there is someone waiting behind */
+
+ if (trx->has_search_latch
+ && btr_search_latch.writer != RW_LOCK_NOT_LOCKED) {
+
+ /* There is an x-latch request on the adaptive hash index:
+ release the s-latch to reduce starvation and wait for
+ BTR_SEA_TIMEOUT rounds before trying to keep it again over
+ calls from MySQL */
+
+ rw_lock_s_unlock(&btr_search_latch);
+ trx->has_search_latch = FALSE;
+
+ trx->search_latch_timeout = BTR_SEA_TIMEOUT;
+ }
+
+ /*-------------------------------------------------------------*/
/* PHASE 1: Try to pop the row from the prefetch cache */
if (direction == 0) {
@@ -2736,23 +2754,7 @@ row_search_for_mysql(
NOT prepared to inserts interleaved with the SELECT,
and if we try that, we can deadlock on the adaptive
hash index semaphore! */
-
- if (btr_search_latch.writer != RW_LOCK_NOT_LOCKED) {
- /* There is an x-latch request: release
- a possible s-latch to reduce starvation
- and wait for BTR_SEA_TIMEOUT rounds before
- trying to keep it again over calls from
- MySQL */
-
- if (trx->has_search_latch) {
- rw_lock_s_unlock(&btr_search_latch);
- trx->has_search_latch = FALSE;
- }
- trx->search_latch_timeout = BTR_SEA_TIMEOUT;
-
- goto no_shortcut;
- }
#ifndef UNIV_SEARCH_DEBUG
if (!trx->has_search_latch) {
rw_lock_s_lock(&btr_search_latch);
@@ -2810,7 +2812,6 @@ row_search_for_mysql(
}
}
-no_shortcut:
/*-------------------------------------------------------------*/
/* PHASE 3: Open or restore index cursor position */
diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c
index 64569bf3f96..5fce1c1861b 100644
--- a/innobase/row/row0upd.c
+++ b/innobase/row/row0upd.c
@@ -218,7 +218,7 @@ row_upd_check_references_constraints(
being dropped while the check is running. */
err = row_ins_check_foreign_constraint(FALSE, foreign,
- table, index, entry, thr);
+ table, entry, thr);
if (foreign->foreign_table) {
mutex_enter(&(dict_sys->mutex));