Commit 79b79064 authored by unknown's avatar unknown

Merge bk-internal.mysql.com:/home/bk/mysql-5.0

into  mysql.com:/home/dlenev/src/mysql-5.0-bg13407

parents 52f22828 f132ab56
......@@ -2189,7 +2189,7 @@ dict_foreign_error_report(
dict_foreign_error_report_low(file, fk->foreign_table_name);
fputs(msg, file);
fputs(" Constraint:\n", file);
dict_print_info_on_foreign_key_in_create_format(file, NULL, fk);
dict_print_info_on_foreign_key_in_create_format(file, NULL, fk, TRUE);
if (fk->foreign_index) {
fputs("\nThe index in the foreign key in table is ", file);
ut_print_name(file, NULL, fk->foreign_index->name);
......@@ -4332,7 +4332,8 @@ dict_print_info_on_foreign_key_in_create_format(
/*============================================*/
FILE* file, /* in: file where to print */
trx_t* trx, /* in: transaction */
dict_foreign_t* foreign)/* in: foreign key constraint */
dict_foreign_t* foreign, /* in: foreign key constraint */
ibool add_newline) /* in: whether to add a newline */
{
const char* stripped_id;
ulint i;
......@@ -4345,7 +4346,16 @@ dict_print_info_on_foreign_key_in_create_format(
stripped_id = foreign->id;
}
fputs(",\n CONSTRAINT ", file);
putc(',', file);
if (add_newline) {
/* SHOW CREATE TABLE wants constraints each printed nicely
on its own line, while error messages want no newlines
inserted. */
fputs("\n ", file);
}
fputs(" CONSTRAINT ", file);
ut_print_name(file, trx, stripped_id);
fputs(" FOREIGN KEY (", file);
......@@ -4447,7 +4457,7 @@ dict_print_info_on_foreign_keys(
while (foreign != NULL) {
if (create_table_format) {
dict_print_info_on_foreign_key_in_create_format(
file, trx, foreign);
file, trx, foreign, TRUE);
} else {
ulint i;
fputs("; (", file);
......
......@@ -377,7 +377,8 @@ dict_print_info_on_foreign_key_in_create_format(
/*============================================*/
FILE* file, /* in: file where to print */
trx_t* trx, /* in: transaction */
dict_foreign_t* foreign);/* in: foreign key constraint */
dict_foreign_t* foreign, /* in: foreign key constraint */
ibool add_newline); /* in: whether to add a newline */
/************************************************************************
Displays the names of the index and the table. */
void
......
......@@ -432,6 +432,17 @@ os_file_read(
offset */
ulint n); /* in: number of bytes to read */
/***********************************************************************
Rewind file to its start, read at most size - 1 bytes from it to str, and
NUL-terminate str. All errors are silently ignored. This function is
mostly meant to be used with temporary files. */
void
os_file_read_string(
/*================*/
FILE* file, /* in: file to read from */
char* str, /* in: buffer where to read */
ulint size); /* in: size of buffer */
/***********************************************************************
Requests a synchronous positioned read operation. This function does not do
any error handling. In case of error it returns FALSE. */
......
......@@ -56,6 +56,22 @@ void
trx_search_latch_release_if_reserved(
/*=================================*/
trx_t* trx); /* in: transaction */
/**********************************************************************
Set detailed error message for the transaction. */
void
trx_set_detailed_error(
/*===================*/
trx_t* trx, /* in: transaction struct */
char* msg); /* in: detailed error message */
/*****************************************************************
Set detailed error message for the transaction from a file. Note that the
file is rewinded before reading from it. */
void
trx_set_detailed_error_from_file(
/*=============================*/
trx_t* trx, /* in: transaction struct */
FILE* file); /* in: file to read message from */
/********************************************************************
Retrieves the error_info field from a trx. */
......@@ -649,6 +665,9 @@ struct trx_struct{
trx_undo_arr_t* undo_no_arr; /* array of undo numbers of undo log
records which are currently processed
by a rollback operation */
/*------------------------------*/
char detailed_error[256]; /* detailed error message for last
error, or empty. */
};
#define TRX_MAX_N_THREADS 32 /* maximum number of concurrent
......
......@@ -118,6 +118,18 @@ UNIV_INLINE
int
ut_strcmp(const void* str1, const void* str2);
/**************************************************************************
Copies up to size - 1 characters from the NUL-terminated string src to
dst, NUL-terminating the result. Returns strlen(src), so truncation
occurred if the return value >= size. */
ulint
ut_strlcpy(
/*=======*/
/* out: strlen(src) */
char* dst, /* in: destination buffer */
const char* src, /* in: source buffer */
ulint size); /* in: size of destination buffer */
/**************************************************************************
Compute strlen(ut_strcpyq(str, q)). */
UNIV_INLINE
......
......@@ -2248,6 +2248,29 @@ error_handling:
return(FALSE);
}
/***********************************************************************
Rewind file to its start, read at most size - 1 bytes from it to str, and
NUL-terminate str. All errors are silently ignored. This function is
mostly meant to be used with temporary files. */
void
os_file_read_string(
/*================*/
FILE* file, /* in: file to read from */
char* str, /* in: buffer where to read */
ulint size) /* in: size of buffer */
{
size_t flen;
if (size == 0) {
return;
}
rewind(file);
flen = fread(str, 1, size - 1, file);
str[flen] = '\0';
}
/***********************************************************************
Requests a synchronous write operation. */
......
......@@ -578,6 +578,30 @@ row_ins_cascade_calc_update_vec(
return(n_fields_updated);
}
/*************************************************************************
Set detailed error message associated with foreign key errors for
the given transaction. */
static
void
row_ins_set_detailed(
/*=================*/
trx_t* trx, /* in: transaction */
dict_foreign_t* foreign) /* in: foreign key constraint */
{
FILE* tf = os_file_create_tmpfile();
ut_a(tf);
ut_print_name(tf, trx, foreign->foreign_table_name);
dict_print_info_on_foreign_key_in_create_format(tf, trx,
foreign, FALSE);
trx_set_detailed_error_from_file(trx, tf);
fclose(tf);
}
/*************************************************************************
Reports a foreign key error associated with an update or a delete of a
parent table index entry. */
......@@ -598,6 +622,8 @@ row_ins_foreign_report_err(
FILE* ef = dict_foreign_err_file;
trx_t* trx = thr_get_trx(thr);
row_ins_set_detailed(trx, foreign);
mutex_enter(&dict_foreign_err_mutex);
rewind(ef);
ut_print_timestamp(ef);
......@@ -607,7 +633,8 @@ row_ins_foreign_report_err(
fputs("Foreign key constraint fails for table ", ef);
ut_print_name(ef, trx, foreign->foreign_table_name);
fputs(":\n", ef);
dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign);
dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign,
TRUE);
putc('\n', ef);
fputs(errstr, ef);
fputs(" in parent table, in index ", ef);
......@@ -649,6 +676,8 @@ row_ins_foreign_report_add_err(
{
FILE* ef = dict_foreign_err_file;
row_ins_set_detailed(trx, foreign);
mutex_enter(&dict_foreign_err_mutex);
rewind(ef);
ut_print_timestamp(ef);
......@@ -657,7 +686,8 @@ row_ins_foreign_report_add_err(
fputs("Foreign key constraint fails for table ", ef);
ut_print_name(ef, trx, foreign->foreign_table_name);
fputs(":\n", ef);
dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign);
dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign,
TRUE);
fputs("\nTrying to add in child table, in index ", ef);
ut_print_name(ef, trx, foreign->foreign_index->name);
if (entry) {
......@@ -1223,6 +1253,8 @@ run_again:
if (check_table == NULL || check_table->ibd_file_missing) {
if (check_ref) {
row_ins_set_detailed(trx, foreign);
FILE* ef = dict_foreign_err_file;
mutex_enter(&dict_foreign_err_mutex);
rewind(ef);
......@@ -1233,7 +1265,7 @@ run_again:
ut_print_name(ef, trx, foreign->foreign_table_name);
fputs(":\n", ef);
dict_print_info_on_foreign_key_in_create_format(ef,
trx, foreign);
trx, foreign, TRUE);
fputs("\nTrying to add to index ", ef);
ut_print_name(ef, trx, foreign->foreign_index->name);
fputs(" tuple:\n", ef);
......
......@@ -52,6 +52,32 @@ trx_start_if_not_started_noninline(
trx_start_if_not_started(trx);
}
/*****************************************************************
Set detailed error message for the transaction. */
void
trx_set_detailed_error(
/*===================*/
trx_t* trx, /* in: transaction struct */
char* msg) /* in: detailed error message */
{
ut_strlcpy(trx->detailed_error, msg, sizeof(trx->detailed_error));
}
/*****************************************************************
Set detailed error message for the transaction from a file. Note that the
file is rewinded before reading from it. */
void
trx_set_detailed_error_from_file(
/*=============================*/
trx_t* trx, /* in: transaction struct */
FILE* file) /* in: file to read message from */
{
os_file_read_string(file, trx->detailed_error,
sizeof(trx->detailed_error));
}
/********************************************************************
Retrieves the error_info field from a trx. */
......@@ -130,6 +156,7 @@ trx_create(
trx->undo_no_arr = NULL;
trx->error_state = DB_SUCCESS;
trx->detailed_error[0] = '\0';
trx->sess = sess;
trx->que_state = TRX_QUE_RUNNING;
......
......@@ -342,6 +342,31 @@ ut_free_all_mem(void)
}
}
/**************************************************************************
Copies up to size - 1 characters from the NUL-terminated string src to
dst, NUL-terminating the result. Returns strlen(src), so truncation
occurred if the return value >= size. */
ulint
ut_strlcpy(
/*=======*/
/* out: strlen(src) */
char* dst, /* in: destination buffer */
const char* src, /* in: source buffer */
ulint size) /* in: size of destination buffer */
{
ulint src_size = strlen(src);
if (size != 0) {
ulint n = ut_min(src_size, size - 1);
memcpy(dst, src, n);
dst[n] = '\0';
}
return src_size;
}
/**************************************************************************
Make a quoted copy of a NUL-terminated string. Leading and trailing
quotes will not be included; only embedded quotes will be escaped.
......
......@@ -1378,9 +1378,9 @@ insert into `t2`values ( 1 ) ;
create table `t3` (`id` int( 11 ) not null default '0',key `id` ( `id` ) ,constraint `t2_id_fk` foreign key ( `id` ) references `t2` (`id` )) engine = innodb;
insert into `t3`values ( 1 ) ;
delete t3,t2,t1 from t1,t2,t3 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test/t2`, CONSTRAINT `t1_id_fk` FOREIGN KEY (`id`) REFERENCES `t1` (`id`))
update t1,t2,t3 set t3.id=5, t2.id=6, t1.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test/t2`, CONSTRAINT `t1_id_fk` FOREIGN KEY (`id`) REFERENCES `t1` (`id`))
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
ERROR 42S22: Unknown column 't1.id' in 'where clause'
drop table t3,t2,t1;
......@@ -1392,7 +1392,7 @@ foreign key(pid) references t1(id) on delete cascade) engine=innodb;
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
delete from t1 where id=0;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test/t1`, CONSTRAINT `t1_ibfk_1` FOREIGN KEY (`pid`) REFERENCES `t1` (`id`) ON DELETE CASCADE)
delete from t1 where id=15;
delete from t1 where id=0;
drop table t1;
......@@ -2593,3 +2593,26 @@ ERROR HY000: Can't create table './test/t4.frm' (errno: 139)
create table t5 (col1 blob, index(col1(768)))
character set = latin1 engine = innodb;
ERROR HY000: Can't create table './test/t5.frm' (errno: 139)
CREATE TABLE t1
(
id INT PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE t2
(
v INT,
CONSTRAINT c1 FOREIGN KEY (v) REFERENCES t1(id)
) ENGINE=InnoDB;
INSERT INTO t2 VALUES(2);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test/t2`, CONSTRAINT `c1` FOREIGN KEY (`v`) REFERENCES `t1` (`id`))
INSERT INTO t1 VALUES(1);
INSERT INTO t2 VALUES(1);
DELETE FROM t1 WHERE id = 1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test/t2`, CONSTRAINT `c1` FOREIGN KEY (`v`) REFERENCES `t1` (`id`))
DROP TABLE t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE t1;
SET FOREIGN_KEY_CHECKS=1;
INSERT INTO t2 VALUES(3);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test/t2`, CONSTRAINT `c1` FOREIGN KEY (`v`) REFERENCES `t1` (`id`))
DROP TABLE t2;
......@@ -978,9 +978,9 @@ create table `t2` (`id` int( 11 ) not null default '0',unique key `id` ( `id` )
insert into `t2`values ( 1 ) ;
create table `t3` (`id` int( 11 ) not null default '0',key `id` ( `id` ) ,constraint `t2_id_fk` foreign key ( `id` ) references `t2` (`id` )) engine = innodb;
insert into `t3`values ( 1 ) ;
--error 1217
--error 1451
delete t3,t2,t1 from t1,t2,t3 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
--error 1217
--error 1451
update t1,t2,t3 set t3.id=5, t2.id=6, t1.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
--error 1054
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
......@@ -996,7 +996,7 @@ create table t1(
foreign key(pid) references t1(id) on delete cascade) engine=innodb;
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
-- error 1217
-- error 1451
delete from t1 where id=0;
delete from t1 where id=15;
delete from t1 where id=0;
......@@ -1528,3 +1528,39 @@ create table t4 (col1 text, index(col1(768)))
--error 1005
create table t5 (col1 blob, index(col1(768)))
character set = latin1 engine = innodb;
#
# Test improved foreign key error messages (bug #3443)
#
CREATE TABLE t1
(
id INT PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE t2
(
v INT,
CONSTRAINT c1 FOREIGN KEY (v) REFERENCES t1(id)
) ENGINE=InnoDB;
--error 1452
INSERT INTO t2 VALUES(2);
INSERT INTO t1 VALUES(1);
INSERT INTO t2 VALUES(1);
--error 1451
DELETE FROM t1 WHERE id = 1;
--error 1217
DROP TABLE t1;
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE t1;
SET FOREIGN_KEY_CHECKS=1;
--error 1452
INSERT INTO t2 VALUES(3);
DROP TABLE t2;
......@@ -6074,6 +6074,8 @@ ha_innobase::start_stmt(
}
}
trx->detailed_error[0] = '\0';
/* Set the MySQL flag to mark that there is an active transaction */
if (trx->active_trans == 0) {
......@@ -6147,6 +6149,8 @@ ha_innobase::external_lock(
if (lock_type != F_UNLCK) {
/* MySQL is setting a new table lock */
trx->detailed_error[0] = '\0';
/* Set the MySQL flag to mark that there is an active
transaction */
if (trx->active_trans == 0) {
......@@ -6950,6 +6954,18 @@ ha_innobase::reset_auto_increment(ulonglong value)
DBUG_RETURN(0);
}
/* See comment in handler.cc */
bool
ha_innobase::get_error_message(int error, String *buf)
{
trx_t* trx = check_trx_exists(current_thd);
buf->copy(trx->detailed_error, strlen(trx->detailed_error),
system_charset_info);
return FALSE;
}
/***********************************************************************
Compares two 'refs'. A 'ref' is the (internal) primary key value of the row.
If there is no explicitly declared non-null unique key or a primary key, then
......
......@@ -175,6 +175,8 @@ class ha_innobase: public handler
ulonglong get_auto_increment();
int reset_auto_increment(ulonglong value);
virtual bool get_error_message(int error, String *buf);
uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; }
/*
ask handler about permission to cache table during query registration
......
......@@ -344,8 +344,8 @@ static int ha_init_errors(void)
SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER(ER_READ_ONLY_TRANSACTION));
SETMSG(HA_ERR_LOCK_DEADLOCK, ER(ER_LOCK_DEADLOCK));
SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER(ER_CANNOT_ADD_FOREIGN));
SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW));
SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED));
SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW_2));
SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED_2));
SETMSG(HA_ERR_NO_SAVEPOINT, "No savepoint with that name");
SETMSG(HA_ERR_NON_UNIQUE_BLOCK_SIZE, "Non unique key block size");
SETMSG(HA_ERR_NO_SUCH_TABLE, "No such table: '%.64s'");
......@@ -1798,11 +1798,19 @@ void handler::print_error(int error, myf errflag)
textno=ER_CANNOT_ADD_FOREIGN;
break;
case HA_ERR_ROW_IS_REFERENCED:
textno=ER_ROW_IS_REFERENCED;
break;
{
String str;
get_error_message(error, &str);
my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
DBUG_VOID_RETURN;
}
case HA_ERR_NO_REFERENCED_ROW:
textno=ER_NO_REFERENCED_ROW;
break;
{
String str;
get_error_message(error, &str);
my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
DBUG_VOID_RETURN;
}
case HA_ERR_TABLE_DEF_CHANGED:
textno=ER_TABLE_DEF_CHANGED;
break;
......
......@@ -5415,3 +5415,7 @@ ER_NO_SUCH_USER
eng "There is not %-.64s@%-.64s registered"
ER_FORBID_SCHEMA_CHANGE
eng "Changing schema from '%-.64s' to '%-.64s' is not allowed."
ER_ROW_IS_REFERENCED_2 23000
eng "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)"
ER_NO_REFERENCED_ROW_2 23000
eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
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