Bug #18477 | MySQL/InnoDB Ignoring Foreign Keys in ALTER TABLE | ||
---|---|---|---|
Submitted: | 24 Mar 2006 5:47 | Modified: | 11 Apr 2006 2:20 |
Reporter: | Richard Harms | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server | Severity: | S1 (Critical) |
Version: | 5.1.7-beta | OS: | Linux (Fedora Core 4, x86_64) |
Assigned to: | Ingo Strüwing | CPU Architecture: | Any |
[24 Mar 2006 5:47]
Richard Harms
[24 Mar 2006 7:24]
Heikki Tuuri
The reason is that mysql_alter_table() in sql_table.cc sets create_info->frm_only = TRUE, which causes rea_create_table() not to call InnoDB at all. But in the current InnoDB implementation of foreign keys it is necessary to rebuild the whole table. During the rebuild we also check that the new foreign key constraint is satisified by the table. (gdb) print create_info->frm_only $1 = true (gdb) step 338 DBUG_RETURN(0); (gdb) 343 } /* rea_create_table */ (gdb) bt #0 rea_create_table (thd=0x8f63038, path=0x4a921990 "./test/@0023sql@002d3aa2_1", db=0x8f49598 "test", table_name=0x4a922c20 "#sql-3aa2_1", create_info=0x8f635e4, create_fields=@0x0, keys=0, key_info=0x0, file=0x8f79c08) at unireg.cc:343 #1 0x0829b5f4 in mysql_create_table_internal (thd=0x8f63038, db=0x8f49598 "test", table_name=0x4a922c20 "#sql-3aa2_1", create_info=0x8f635e4, fields=@0x4a921f90, keys=@0x8f635e4, internal_tmp_table=true, select_field_count=0) at sql_table.cc:2243 #2 0x0829baad in mysql_create_table (thd=0x8f63038, db=0x8f49598 "test", table_name=0x0, create_info=0x0, fields=@0x0, keys=@0x0, internal_tmp_table=true, select_field_count=0) at sql_table.cc:2320 #3 0x0829e4c8 in mysql_alter_table (thd=0x8f63038, new_db=0x8f49598 "test", new_name=0x8f79600 "tablea", create_info=0x8f635e4, table_list=0x8f79628, fields=@0x4a922c20, keys=@0x8f634f0, order_num=0, order=0x0, handle_duplicates=DUP_ERROR, ignore=false, alter_info=0x8f6372c, do_send_ok=true) at sql_table.cc:4653 #4 0x081e4c0e in mysql_execute_command (thd=0x8f63038) at sql_parse.cc:3049 #5 0x081ed0a9 in mysql_parse (thd=0x8f63038, inBuf=0x8f63074 "\020P\\\b\001", length=111) at sql_parse.cc:5882 #6 0x081e26e1 in dispatch_command (command=150351928, thd=0x8f63038, packet=0x8f91551 "ALTER TABLE tablea ADD CONSTRAINT ta_tb_id_fk FOREIGN KEY (ta_tb_id) REFERENCES tableb(tb_id) ON DELETE CASCADE", packet_length=150443352) at sql_parse.cc:1741 #7 0x081e223d in do_command (thd=0x8f63038) at sql_parse.cc:1537 #8 0x081e15b2 in handle_one_connection (arg=0x8f63038) at sql_parse.cc:1179 #9 0x4016cb63 in start_thread () from /lib/tls/libpthread.so.0 #10 0x402b318a in clone () from /lib/tls/libc.so.6 (gdb) Fix: compare_tables() in sql_table.cc should return ALTER_TABLE_DATA_CHANGED if a new FOREIGN KEY constraint is added in the ALTER.
[30 Mar 2006 7:26]
Ingo Strüwing
The problem is the following: The SQL layer ignores foreign key definitions except for syntax checks. It does not store foreign key information in its table structures and hence not in the .frm file. The InnoDB table handler extracts (parses) the CREATE/ALTER TABLE statement to get at the foreign key information. For SHOW CREATE TABLE the InnoDB table handler creates the textual information regarding foreign keys. The consequence is that in compare_tables() it is not possible to detect if the old table has foreign keys. Hence it cannot be detected if a foreign key is to be dropped. I could detect that the new table has to have a foreign key, but I cannot decide if this is a new one, a changed one, or the same as the in the old table. The only way out is to make use of the handler::check_if_incompatible_data() method, which is called at a last ressort at the end of compare_tables(). This method needs to be extended in ha_innodb.cc to detect foreign key changes. Since changing the InnoDB table handler is new to me, I estimate a two weeks time frame.
[30 Mar 2006 8:40]
Heikki Tuuri
Ingo, another solution: add a flag to thd->lex to denote that ALTER TABLE contained a FOREIGN KEY creation clause or a DROP FOREIGN KEY clause. If that is the case, then compare_tables() should say that the table has to be rebuilt. You have to modify the MySQL SQL parser a little. I guess this is fix requires one day of work. It was my intention to submit a patch to MySQL, but I have been busy with other bugs for the past 3 days. Regards, Heikki
[30 Mar 2006 18:29]
Ingo Strüwing
Heikkis proposal seems to work. Who ever merged the fix for Bug#16387 (InnoDB crash when dropping a foreign key <table>_ibfk_0) to 5.1 could have detected this bug earlier. The result file showed the wrong behaviour.
[30 Mar 2006 19:10]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/4339
[6 Apr 2006 7:03]
Ingo Strüwing
Patch approved by Heikki Tuuri.
[8 Apr 2006 13:22]
Ingo Strüwing
InnoDB requires a full table rebuild for foreign key changes. It was not possible in compare_tables() to detect such changes. On Heikkis proposal I added a new flag to the syntax parser where foreign key definition changes are done. I test for this flag in compare_tables() now. Pushed to 5.1.10.
[11 Apr 2006 2:20]
Paul DuBois
Noted in 5.1.10 changelog. <literal>InnoDB</literal>: <literal>ALTER TABLE</literal> to add or drop a foreign key for an <literal>InnoDB</literal> table had no effect. (Bug #18477)