List:Commits« Previous MessageNext Message »
From:vasil.dimov Date:October 21 2011 11:01am
Subject:bzr push into mysql-trunk branch (vasil.dimov:3519 to 3520) Bug#12661768
View as plain text  
 3520 Vasil Dimov	2011-10-21
      Fix Bug#12661768 UPDATE IGNORE CRASHES SERVER IF TABLE IS INNODB AND IT IS
      PARENT FOR OTHER ONE
      
      When "UPDATE t1 SET c = 1" is prevented by a duplicate entry in t2 (where
      there is a FK on t1 and t2 and ON UPDATE CASCADE), then InnoDB returns the
      number of the offending index from t2, but the MySQL part of the code
      assumes this is the Nth index from t1. If t1 contains less indexes, then a
      crash may occur, or if the returned number is less than the number of the
      indexes - then a bogus data is printed.
      
      Since MySQL is not aware of the second table, the option here is to return
      its name and the index name from InnoDB to MySQL instead of just a bare
      number (designating the number of the index).
      
      Approved by:	Jimmy, Marko, Jon Olav Hauglid (rb://768)

    added:
      mysql-test/suite/innodb/r/innodb_bug12661768.result
      mysql-test/suite/innodb/t/innodb_bug12661768.test
    modified:
      mysql-test/include/mix2.inc
      mysql-test/suite/innodb/r/innodb.result
      mysql-test/suite/innodb/r/innodb_bug53592.result
      mysql-test/suite/innodb/t/innodb.test
      mysql-test/suite/innodb/t/innodb_bug53592.test
      sql/handler.cc
      sql/handler.h
      sql/share/errmsg-utf8.txt
      storage/innobase/handler/ha_innodb.cc
      storage/innobase/handler/ha_innodb.h
 3519 Sergey Vojtovich	2011-10-21 [merge]
      Merge.

    modified:
      mysql-test/r/myisam.result
      mysql-test/t/myisam.test
      storage/myisam/ha_myisam.cc
      storage/myisam/mi_delete_all.c
=== modified file 'mysql-test/include/mix2.inc'
--- a/mysql-test/include/mix2.inc	revid:sergey.vojtovich@stripped
+++ b/mysql-test/include/mix2.inc	revid:vasil.dimov@stripped111021105028-0qlfem4n84d6uf2l
@@ -2099,7 +2099,7 @@ INSERT INTO t1 VALUES ('other', 'anyvalu
 INSERT INTO t2 VALUES ('old');
 INSERT INTO t2 VALUES ('other');
 
---error ER_FOREIGN_DUPLICATE_KEY
+--error ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO
 UPDATE t1 SET field1 = 'other' WHERE field2 = 'somevalu';
 
 DROP TABLE t2;

=== modified file 'mysql-test/suite/innodb/r/innodb.result'
--- a/mysql-test/suite/innodb/r/innodb.result	revid:sergey.vojtovich@oracle.com-20111021054201-46yom9gu0c0tjbwh
+++ b/mysql-test/suite/innodb/r/innodb.result	revid:vasil.dimov@stripped20111021105028-0qlfem4n84d6uf2l
@@ -2772,7 +2772,7 @@ INSERT INTO t1 VALUES ('other', 'anyvalu
 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
+ERROR 23000: Foreign key constraint for table 't1', record 'other-somevalu' would lead to a duplicate entry in table 't2', key 'PRIMARY'
 DROP TABLE t2;
 DROP TABLE t1;
 create table t1 (

=== added file 'mysql-test/suite/innodb/r/innodb_bug12661768.result'
--- a/mysql-test/suite/innodb/r/innodb_bug12661768.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/r/innodb_bug12661768.result	revid:vasil.dimov@strippedn84d6uf2l
@@ -0,0 +1,2 @@
+SET SESSION foreign_key_checks=0;
+ERROR 23000: Foreign key constraint for table 'bug12661768_1��1111111111111111111111111111111111111111111111111', record '3-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' would lead to a duplicate entry in table 'bug12661768_2��2222222222222222222222222222222222222222222222222', key 'ab_on_2_fkfkf��fkffkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfk'

=== modified file 'mysql-test/suite/innodb/r/innodb_bug53592.result'
--- a/mysql-test/suite/innodb/r/innodb_bug53592.result	revid:sergey.vojtovich@stripped01-46yom9gu0c0tjbwh
+++ b/mysql-test/suite/innodb/r/innodb_bug53592.result	revid:vasil.dimov@stripped5028-0qlfem4n84d6uf2l
@@ -38,6 +38,6 @@ INSERT INTO bug53592_1 VALUES (3, 4);
 INSERT INTO bug53592_2 VALUES (1);
 INSERT INTO bug53592_2 VALUES (3);
 UPDATE bug53592_1 SET col1 = 3 WHERE col2 = 2;
-ERROR 23000: Upholding foreign key constraints for table 'bug53592_1', entry '3-2', key 1 would lead to a duplicate entry
+ERROR 23000: Foreign key constraint for table 'bug53592_1', record '3-2' would lead to a duplicate entry in table 'bug53592_2', key 'PRIMARY'
 drop table bug53592_2;
 drop table bug53592_1;

=== modified file 'mysql-test/suite/innodb/t/innodb.test'
--- a/mysql-test/suite/innodb/t/innodb.test	revid:sergey.vojtovich@stripped
+++ b/mysql-test/suite/innodb/t/innodb.test	revid:vasil.dimov@stripped
@@ -1963,7 +1963,7 @@ INSERT INTO t1 VALUES ('other', 'anyvalu
 INSERT INTO t2 VALUES ('old');
 INSERT INTO t2 VALUES ('other');
 
---error ER_FOREIGN_DUPLICATE_KEY
+--error ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO
 UPDATE t1 SET field1 = 'other' WHERE field2 = 'somevalu';
 
 DROP TABLE t2;

=== added file 'mysql-test/suite/innodb/t/innodb_bug12661768.test'
--- a/mysql-test/suite/innodb/t/innodb_bug12661768.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/t/innodb_bug12661768.test	revid:vasil.dimov@strippedm-20111021105028-0qlfem4n84d6uf2l
@@ -0,0 +1,46 @@
+#
+# Bug#12661768 UPDATE IGNORE CRASHES SERVER IF TABLE IS INNODB AND IT IS
+# PARENT FOR OTHER ONE
+#
+
+SET SESSION foreign_key_checks=0;
+
+# only interested that the "UPDATE IGNORE" at the end does not crash the server
+
+-- disable_query_log
+-- disable_result_log
+
+-- let $t1_name = bug12661768_1��1111111111111111111111111111111111111111111111111
+-- let $t2_name = bug12661768_2��2222222222222222222222222222222222222222222222222
+-- let $fk_name = ab_on_2_fkfkf��fkffkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfkfk
+-- let $key_str = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
+
+eval DROP TABLE IF EXISTS `$t2_name`, `$t1_name`;
+
+eval CREATE TABLE `$t1_name` (
+	a INT,
+	b VARCHAR(512),
+	PRIMARY KEY (a, b)
+) ENGINE=INNODB;
+
+eval CREATE TABLE `$t2_name` (
+	id INT,
+	a INT,
+	b VARCHAR(512),
+	PRIMARY KEY (id),
+	UNIQUE KEY `$fk_name` (a, b),
+	FOREIGN KEY (a, b) REFERENCES `$t1_name` (a, b)
+	ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=INNODB;
+
+eval INSERT INTO `$t1_name` VALUES (1, $key_str);
+eval INSERT INTO `$t2_name` VALUES (100, 1, $key_str), (101, 3, $key_str);
+
+SET SESSION foreign_key_checks=1;
+
+-- enable_result_log
+
+-- error ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO
+eval UPDATE IGNORE `$t1_name` SET a = 3;
+
+eval DROP TABLE `$t2_name`, `$t1_name`;

=== modified file 'mysql-test/suite/innodb/t/innodb_bug53592.test'
--- a/mysql-test/suite/innodb/t/innodb_bug53592.test	revid:sergey.vojtovich@stripped054201-46yom9gu0c0tjbwh
+++ b/mysql-test/suite/innodb/t/innodb_bug53592.test	revid:vasil.dimov@stripped105028-0qlfem4n84d6uf2l
@@ -75,7 +75,7 @@ INSERT INTO bug53592_1 VALUES (3, 4);
 INSERT INTO bug53592_2 VALUES (1);
 INSERT INTO bug53592_2 VALUES (3);
 
---error ER_FOREIGN_DUPLICATE_KEY
+--error ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO
 UPDATE bug53592_1 SET col1 = 3 WHERE col2 = 2;
 
 drop table bug53592_2;

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	revid:sergey.vojtovich@oracle.com-20111021054201-46yom9gu0c0tjbwh
+++ b/sql/handler.cc	revid:vasil.dimov@strippedf2l
@@ -2984,28 +2984,31 @@ void handler::print_error(int error, myf
   }
   case HA_ERR_FOREIGN_DUPLICATE_KEY:
   {
-    uint key_nr= get_dup_key(error);
-    if ((int) key_nr >= 0)
+    DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+                m_lock_type != F_UNLCK);
+
+    char rec_buf[MAX_KEY_LENGTH];
+    String rec(rec_buf, sizeof(rec_buf), system_charset_info);
+    /* Table is opened and defined at this point */
+    key_unpack(&rec, table, 0 /* just print the subset of fields that are
+                              part of the first index, printing the whole
+                              row from there is not easy */);
+
+    char child_table_name[NAME_LEN + 1];
+    char child_key_name[NAME_LEN + 1];
+    if (get_foreign_dup_key(child_table_name, sizeof(child_table_name),
+                            child_key_name, sizeof(child_key_name)))
+    {
+      my_error(ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO, MYF(0),
+               table_share->table_name.str, rec.c_ptr_safe(),
+               child_table_name, child_key_name);
+    }
+    else
     {
-      uint max_length;
-      /* 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);
-      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_safe(), key_nr+1);
-      DBUG_VOID_RETURN;
+      my_error(ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO, MYF(0),
+               table_share->table_name.str, rec.c_ptr_safe());
     }
-    textno= ER_DUP_KEY;
-    break;
+    DBUG_VOID_RETURN;
   }
   case HA_ERR_NULL_IN_SPATIAL:
     my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0));
@@ -3326,7 +3329,7 @@ uint handler::get_dup_key(int error)
               m_lock_type != F_UNLCK);
   DBUG_ENTER("handler::get_dup_key");
   table->file->errkey  = (uint) -1;
-  if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
+  if (error == HA_ERR_FOUND_DUPP_KEY ||
       error == HA_ERR_FOUND_DUPP_UNIQUE || error == HA_ERR_NULL_IN_SPATIAL ||
       error == HA_ERR_DROP_INDEX_FK)
     table->file->info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);

=== modified file 'sql/handler.h'
--- a/sql/handler.h	revid:sergey.vojtovich@strippedh
+++ b/sql/handler.h	revid:vasil.dimov@stripped21105028-0qlfem4n84d6uf2l
@@ -1566,6 +1566,29 @@ public:
   virtual void print_error(int error, myf errflag);
   virtual bool get_error_message(int error, String *buf);
   uint get_dup_key(int error);
+  /**
+    Retrieves the names of the table and the key for which there was a
+    duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY.
+
+    If any of the table or key name is not available this method will return
+    false and will not change any of child_table_name or child_key_name.
+
+    @param child_table_name[out]    Table name
+    @param child_table_name_len[in] Table name buffer size
+    @param child_key_name[out]      Key name
+    @param child_key_name_len[in]   Key name buffer size
+
+    @retval  true                  table and key names were available
+                                   and were written into the corresponding
+                                   out parameters.
+    @retval  false                 table and key names were not available,
+                                   the out parameters were not touched.
+  */
+  virtual bool get_foreign_dup_key(char *child_table_name,
+                                   uint child_table_name_len,
+                                   char *child_key_name,
+                                   uint child_key_name_len)
+  { DBUG_ASSERT(false); return(false); }
   virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
   {
     table= table_arg;

=== modified file 'sql/share/errmsg-utf8.txt'
--- a/sql/share/errmsg-utf8.txt	revid:sergey.vojtovich@strippedh
+++ b/sql/share/errmsg-utf8.txt	revid:vasil.dimov@oracle.com-20111021105028-0qlfem4n84d6uf2l
@@ -5949,9 +5949,8 @@ ER_CANT_WRITE_LOCK_LOG_TABLE
 ER_CANT_LOCK_LOG_TABLE
         eng "You can't use locks with log tables."
         ger "Log-Tabellen k��nnen nicht gesperrt werden."
-ER_FOREIGN_DUPLICATE_KEY 23000 S1009
+ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED 23000 S1009
         eng "Upholding foreign key constraints for table '%.192s', entry '%-.192s', key %d would lead to a duplicate entry"
-        ger "Aufrechterhalten der Fremdschl��ssel-Beschr��nkungen f��r Tabelle '%.192s', Eintrag '%-.192s', Schl�n"
 ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
         eng "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error."
         ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d erhalten. Erzeugt mit MySQL %d, jetzt unter %d. Bitte benutzen Sie mysql_upgrade, um den Fehler zu beheben"
@@ -6598,3 +6597,13 @@ ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITION
 ER_DA_INVALID_CONDITION_NUMBER 35000
   eng "Invalid condition number"
   por "N��mero de condi����o inv��lido"
+
+ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO 23000 S1009
+        eng "Foreign key constraint for table '%.192s', record '%-.192s' would lead to a duplicate entry in table '%.192s', key '%.192s'"
+        ger "Fremdschl��ssel-Beschr��nkung f��r Tabelle '%.192s', Datensatz '%-.192s' w��rde zu einem doppelten Eintrag in Tabelle '%.192s', Schl��ssel '%.192s' f��hren"
+        swe "FOREIGN KEY constraint f��r tabell '%.192s', posten '%-.192s' kan inte uppdatera barntabell '%.192s' pY_WITHOUT_CHILD_INFO 23000 S1009
+        eng "Foreign key constraint for table '%.192s', record '%-.192s' would lead to a duplicate entry in a child table"
+        ger "Fremdschl��ssel-Beschr��nkung f��r Tabelle '%.192s', Datensatz '%-.192s' w��rde zu einem doppelten Eintrag in einer Kind-Tabelle f��hren"
+        swe "FOREIGN KEY constraint f��r tabell '%.192s', posten '%-.192s' kan inte uppdatera en barntabell p�� grund av UNIQUE-test"

=== modified file 'storage/innobase/handler/ha_innodb.cc'
--- a/storage/innobase/handler/ha_innodb.cc	revid:sergey.vojtovich@strippedom9gu0c0tjbwh
+++ b/storage/innobase/handler/ha_innodb.cc	revid:vasil.dimov@strippeduf2l
@@ -10671,6 +10671,72 @@ ha_innobase::get_error_message(
 }
 
 /*******************************************************************//**
+  Retrieves the names of the table and the key for which there was a
+  duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY.
+
+  If any of the names is not available, then this method will return
+  false and will not change any of child_table_name or child_key_name.
+
+  @param child_table_name[out]    Table name
+  @param child_table_name_len[in] Table name buffer size
+  @param child_key_name[out]      Key name
+  @param child_key_name_len[in]   Key name buffer size
+
+  @retval  true                  table and key names were available
+                                 and were written into the corresponding
+                                 out parameters.
+  @retval  false                 table and key names were not available,
+                                 the out parameters were not touched.
+*/
+bool
+ha_innobase::get_foreign_dup_key(
+/*=============================*/
+	char*	child_table_name,
+	uint	child_table_name_len,
+	char*	child_key_name,
+	uint	child_key_name_len)
+{
+	const dict_index_t*	err_index;
+
+	ut_a(prebuilt->trx != NULL);
+	ut_a(prebuilt->trx->magic_n == TRX_MAGIC_N);
+
+	err_index = trx_get_error_info(prebuilt->trx);
+
+	if (err_index == NULL) {
+		return(false);
+	}
+	/* else */
+
+	/* copy table name (and convert from filename-safe encoding to
+	system_charset_info, e.g. "foo_@0J@00b6" -> "foo_��") */
+	char*	p;
+	p = strchr(err_index->table->name, '/');
+	/* strip ".../" prefix if any */
+	if (p != NULL) {
+		p++;
+	} else {
+		p = err_index->table->name;
+	}
+	innobase_convert_name(child_table_name, child_table_name_len,
+			      p, strlen(p), NULL, TRUE);
+	/* remove quotes if any */
+	if (child_table_name[0] == '"'
+	    && child_table_name[strlen(child_table_name) - 1] == '"') {
+		size_t	len_no_quotes = strlen(child_table_name) - 2;
+		/* "abc" -> abcc" */
+		memmove(child_table_name, child_table_name + 1, len_no_quotes);
+		/* abcc" -> abc */
+		child_table_name[len_no_quotes] = '\0';
+	}
+
+	/* copy index name */
+	ut_snprintf(child_key_name, child_key_name_len, "%s", err_index->name);
+
+	return(true);
+}
+
+/*******************************************************************//**
 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
 InnoDB internally uses the row id as the primary key.

=== modified file 'storage/innobase/handler/ha_innodb.h'
--- a/storage/innobase/handler/ha_innodb.h	revid:sergey.vojtovich@stripped6yom9gu0c0tjbwh
+++ b/storage/innobase/handler/ha_innodb.h	revid:vasil.dimov@stripped6uf2l
@@ -199,7 +199,7 @@ class ha_innobase: public handler
 	int reset_auto_increment(ulonglong value);
 
 	virtual bool get_error_message(int error, String *buf);
-
+	virtual bool get_foreign_dup_key(char*, uint, char*, uint);
 	uint8 table_cache_type();
 	/*
 	  ask handler about permission to cache table during query registration

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (vasil.dimov:3519 to 3520) Bug#12661768vasil.dimov24 Oct