Commit e22d2f11 authored by aivanov@mysql.com's avatar aivanov@mysql.com

Fixed BUG#9680: Wrong error from cascading update

  Applied 9680.patch (by Osku Salerma)
parent a906e3c6
...@@ -367,8 +367,11 @@ enum ha_base_keytype { ...@@ -367,8 +367,11 @@ enum ha_base_keytype {
given value */ given value */
#define HA_ERR_RBR_LOGGING_FAILED 161 /* Row-based binlogging of row failed */ #define HA_ERR_RBR_LOGGING_FAILED 161 /* Row-based binlogging of row failed */
#define HA_ERR_DROP_INDEX_FK 162 /* Index needed in foreign key constr. */ #define HA_ERR_DROP_INDEX_FK 162 /* Index needed in foreign key constr. */
#define HA_ERR_FOREIGN_DUPLICATE_KEY 163 /* Upholding foreign key constraints
would lead to a duplicate key
error in some other table. */
#define HA_ERR_LAST 162 /* Copy last error no */ #define HA_ERR_LAST 163 /* Copy last error no */
/* Add error numbers before HA_ERR_LAST and change it accordingly. */ /* Add error numbers before HA_ERR_LAST and change it accordingly. */
#define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1)
......
...@@ -2758,3 +2758,21 @@ e varchar(255) character set utf8, ...@@ -2758,3 +2758,21 @@ e varchar(255) character set utf8,
key (a,b,c,d,e)) engine=innodb; key (a,b,c,d,e)) engine=innodb;
ERROR 42000: Specified key was too long; max key length is 3072 bytes ERROR 42000: Specified key was too long; max key length is 3072 bytes
End of 5.0 tests End of 5.0 tests
CREATE TABLE t1 (
field1 varchar(8) NOT NULL DEFAULT '',
field2 varchar(8) NOT NULL DEFAULT '',
PRIMARY KEY (field1, field2)
) ENGINE=InnoDB;
CREATE TABLE t2 (
field1 varchar(8) NOT NULL DEFAULT '' PRIMARY KEY,
FOREIGN KEY (field1) REFERENCES t1 (field1)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO t1 VALUES ('old', 'somevalu');
INSERT INTO t1 VALUES ('other', 'anyvalue');
INSERT INTO t2 VALUES ('old');
INSERT INTO t2 VALUES ('other');
UPDATE t1 SET field1 = 'other' WHERE field2 = 'somevalu';
ERROR 23000: Upholding foreign key constraints for table 't1', entry 'other-somevalu', key 1 would lead to a duplicate entry
DROP TABLE t2;
DROP TABLE t1;
...@@ -1715,3 +1715,32 @@ create table t1 (a varchar(255) character set utf8, ...@@ -1715,3 +1715,32 @@ create table t1 (a varchar(255) character set utf8,
key (a,b,c,d,e)) engine=innodb; key (a,b,c,d,e)) engine=innodb;
--echo End of 5.0 tests --echo End of 5.0 tests
#
# Test that cascading updates leading to duplicate keys give the correct
# error message (bug #9680)
#
CREATE TABLE t1 (
field1 varchar(8) NOT NULL DEFAULT '',
field2 varchar(8) NOT NULL DEFAULT '',
PRIMARY KEY (field1, field2)
) ENGINE=InnoDB;
CREATE TABLE t2 (
field1 varchar(8) NOT NULL DEFAULT '' PRIMARY KEY,
FOREIGN KEY (field1) REFERENCES t1 (field1)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO t1 VALUES ('old', 'somevalu');
INSERT INTO t1 VALUES ('other', 'anyvalue');
INSERT INTO t2 VALUES ('old');
INSERT INTO t2 VALUES ('other');
--error ER_FOREIGN_DUPLICATE_KEY
UPDATE t1 SET field1 = 'other' WHERE field2 = 'somevalu';
DROP TABLE t2;
DROP TABLE t1;
...@@ -465,6 +465,10 @@ convert_error_code_to_mysql( ...@@ -465,6 +465,10 @@ convert_error_code_to_mysql(
return(HA_ERR_FOUND_DUPP_KEY); return(HA_ERR_FOUND_DUPP_KEY);
} else if (error == (int) DB_FOREIGN_DUPLICATE_KEY) {
return(HA_ERR_FOREIGN_DUPLICATE_KEY);
} else if (error == (int) DB_RECORD_NOT_FOUND) { } else if (error == (int) DB_RECORD_NOT_FOUND) {
return(HA_ERR_NO_ACTIVE_RECORD); return(HA_ERR_NO_ACTIVE_RECORD);
......
...@@ -358,6 +358,7 @@ static int ha_init_errors(void) ...@@ -358,6 +358,7 @@ static int ha_init_errors(void)
SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR)); SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR));
SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine"); SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine");
SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED)); SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED));
SETMSG(HA_ERR_FOREIGN_DUPLICATE_KEY, "FK constraint would lead to duplicate key");
/* Register the error messages for use with my_error(). */ /* Register the error messages for use with my_error(). */
return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST);
...@@ -1868,6 +1869,29 @@ void handler::print_error(int error, myf errflag) ...@@ -1868,6 +1869,29 @@ void handler::print_error(int error, myf errflag)
textno=ER_DUP_KEY; textno=ER_DUP_KEY;
break; break;
} }
case HA_ERR_FOREIGN_DUPLICATE_KEY:
{
uint key_nr= get_dup_key(error);
if ((int) key_nr >= 0)
{
/* Write the key in the error message */
char key[MAX_KEY_LENGTH];
String str(key,sizeof(key),system_charset_info);
/* Table is opened and defined at this point */
key_unpack(&str,table,(uint) key_nr);
uint max_length= MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY));
if (str.length() >= max_length)
{
str.length(max_length-4);
str.append(STRING_WITH_LEN("..."));
}
my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table_share->table_name.str,
str.c_ptr(), key_nr+1);
DBUG_VOID_RETURN;
}
textno= ER_DUP_KEY;
break;
}
case HA_ERR_NULL_IN_SPATIAL: case HA_ERR_NULL_IN_SPATIAL:
textno= ER_UNKNOWN_ERROR; textno= ER_UNKNOWN_ERROR;
break; break;
...@@ -2003,8 +2027,9 @@ uint handler::get_dup_key(int error) ...@@ -2003,8 +2027,9 @@ uint handler::get_dup_key(int error)
{ {
DBUG_ENTER("handler::get_dup_key"); DBUG_ENTER("handler::get_dup_key");
table->file->errkey = (uint) -1; table->file->errkey = (uint) -1;
if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE || if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
error == HA_ERR_NULL_IN_SPATIAL || error == HA_ERR_DROP_INDEX_FK) error == HA_ERR_FOUND_DUPP_UNIQUE || error == HA_ERR_NULL_IN_SPATIAL ||
error == HA_ERR_DROP_INDEX_FK)
info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK); info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
DBUG_RETURN(table->file->errkey); DBUG_RETURN(table->file->errkey);
} }
......
...@@ -5800,3 +5800,5 @@ ER_CANT_READ_LOCK_LOG_TABLE ...@@ -5800,3 +5800,5 @@ ER_CANT_READ_LOCK_LOG_TABLE
eng "You can't use usual read lock with log tables. Try READ LOCAL instead." eng "You can't use usual read lock with log tables. Try READ LOCAL instead."
ER_SP_WRONG_NAME 42000 ER_SP_WRONG_NAME 42000
eng "Incorrect routine name '%-.64s'" eng "Incorrect routine name '%-.64s'"
ER_FOREIGN_DUPLICATE_KEY 23000 S1009
eng "Upholding foreign key constraints for table '%.64s', entry '%-.64s', key %d would lead to a duplicate entry"
...@@ -57,6 +57,10 @@ Created 5/24/1996 Heikki Tuuri ...@@ -57,6 +57,10 @@ Created 5/24/1996 Heikki Tuuri
buffer pool (for big transactions, buffer pool (for big transactions,
InnoDB stores the lock structs in the InnoDB stores the lock structs in the
buffer pool) */ buffer pool) */
#define DB_FOREIGN_DUPLICATE_KEY 46 /* foreign key constraints
activated by the operation would
lead to a duplicate key in some
table */
/* The following are partial failure codes */ /* The following are partial failure codes */
#define DB_FAIL 1000 #define DB_FAIL 1000
......
...@@ -1376,6 +1376,21 @@ run_again: ...@@ -1376,6 +1376,21 @@ run_again:
thr, foreign, &pcur, entry, thr, foreign, &pcur, entry,
&mtr); &mtr);
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
/* Since reporting a plain
"duplicate key" error
message to the user in
cases where a long CASCADE
operation would lead to a
duplicate key in some
other table is very
confusing, map duplicate
key errors resulting from
FK constraints to a
separate error code. */
if (err == DB_DUPLICATE_KEY) {
err = DB_FOREIGN_DUPLICATE_KEY;
}
break; break;
} }
......
...@@ -473,8 +473,9 @@ handle_new_error: ...@@ -473,8 +473,9 @@ handle_new_error:
ut_a(err != DB_SUCCESS); ut_a(err != DB_SUCCESS);
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
if (err == DB_DUPLICATE_KEY) { if ((err == DB_DUPLICATE_KEY)
|| (err == DB_FOREIGN_DUPLICATE_KEY)) {
if (savept) { if (savept) {
/* Roll back the latest, possibly incomplete /* Roll back the latest, possibly incomplete
insertion or update */ insertion or update */
......
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