Skip to content

Commit 0233670

Browse files
committed
Bug#24666169: I_S.TABLE_CONSTRAINTS.CONSTRAINT_NAME IS NOT UPDATED
AFTER RENAME TABLE Bug#25339192: NEWDD: SERVER SHOULD DISALLOW FOREIGN KEYS ON BASE COLUMN OF STORED COLUMN This patch consists of three parts: 1) When tables are renamed, any foreign keys with generated names are renamed as well. This is necessary since the table name is part of the generated name. We assume that the name was generated if it starts with table_name + '_ibfk_', similar to how InnoDB does it in earlier versions. This part fixes Bug#24666169. 2) During ALTER TABLE, both the new and the old table definition exists in the data dictionary at the same time (uncommitted). In order to satisfy the unique constraint on schema_name+fk_name, pre-existing FKs were before not transferred to the new table definition until ALTER TABLE was almost completed. This meant that FK metadata was not available to InnoDB during ALTER TABLE processing. In order to make it available to InnoDB, foreign keys are now transferred to the new table definintion immediately, but with a temporary name to satisfy the unique constraint. The original name is restored at the end of ALTER TABLE. This part is required to fix Bug#25339192. 3) As a consequence of 2), more validity checking of pre-existing foreign keys had been added to the SQL layer to avoid regressions in error reporting. This consists of checking and reporting ER_FK_DUP_NAME, ER_DROP_INDEX_FK and ER_FK_COLUMN_CANNOT_DROP.
1 parent e95736e commit 0233670

19 files changed

+1470
-246
lines changed

mysql-test/r/foreign_key.result

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,362 @@ CREATE TABLE t1(a INT PRIMARY KEY, b INT,
160160
FOREIGN KEY(b) REFERENCES t2(name5678901234567890123456789012345678901234567890123456789012345));
161161
ERROR 42000: Incorrect column name 'name5678901234567890123456789012345678901234567890123456789012345'
162162
SET @@foreign_key_checks= DEFAULT;
163+
#
164+
# Bug#24666169: I_S.TABLE_CONSTRAINTS.CONSTRAINT_NAME IS NOT UPDATED
165+
# AFTER RENAME TABLE
166+
#
167+
SET @@foreign_key_checks= 1;
168+
#
169+
# Tests for FK name behavior.
170+
CREATE TABLE t1(c1 INT PRIMARY KEY);
171+
CREATE TABLE t2(c1 INT, FOREIGN KEY (c1) REFERENCES t1(c1));
172+
ALTER TABLE t2 RENAME TO t3;
173+
SHOW CREATE TABLE t3;
174+
Table Create Table
175+
t3 CREATE TABLE `t3` (
176+
`c1` int(11) DEFAULT NULL,
177+
KEY `c1` (`c1`),
178+
CONSTRAINT `t3_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`)
179+
) ENGINE=InnoDB DEFAULT CHARSET=latin1
180+
INSERT INTO t3 VALUES(1);
181+
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t3`, CONSTRAINT `t3_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`))
182+
ALTER TABLE t3 RENAME TO t4, ALGORITHM= INPLACE;
183+
SHOW CREATE TABLE t4;
184+
Table Create Table
185+
t4 CREATE TABLE `t4` (
186+
`c1` int(11) DEFAULT NULL,
187+
KEY `c1` (`c1`),
188+
CONSTRAINT `t4_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`)
189+
) ENGINE=InnoDB DEFAULT CHARSET=latin1
190+
INSERT INTO t4 VALUES(1);
191+
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t4`, CONSTRAINT `t4_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`))
192+
ALTER TABLE t4 RENAME TO t5;
193+
SHOW CREATE TABLE t5;
194+
Table Create Table
195+
t5 CREATE TABLE `t5` (
196+
`c1` int(11) DEFAULT NULL,
197+
KEY `c1` (`c1`),
198+
CONSTRAINT `t5_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`)
199+
) ENGINE=InnoDB DEFAULT CHARSET=latin1
200+
INSERT INTO t5 VALUES(1);
201+
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t5`, CONSTRAINT `t5_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`))
202+
RENAME TABLE t5 to t6;
203+
SHOW CREATE TABLE t6;
204+
Table Create Table
205+
t6 CREATE TABLE `t6` (
206+
`c1` int(11) DEFAULT NULL,
207+
KEY `c1` (`c1`),
208+
CONSTRAINT `t6_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`)
209+
) ENGINE=InnoDB DEFAULT CHARSET=latin1
210+
INSERT INTO t6 VALUES(1);
211+
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t6`, CONSTRAINT `t6_ibfk_1` FOREIGN KEY (`c1`) REFERENCES `t1` (`c1`))
212+
DROP TABLE t6, t1;
213+
#
214+
# Tests of FK name generation
215+
CREATE TABLE t1(a INT PRIMARY KEY);
216+
CREATE TABLE t2(a INT, b INT, FOREIGN KEY(a) REFERENCES t1(a));
217+
SELECT constraint_name FROM information_schema.referential_constraints
218+
WHERE table_name = 't2' ORDER BY constraint_name;
219+
constraint_name
220+
t2_ibfk_1
221+
SELECT constraint_name FROM information_schema.table_constraints
222+
WHERE table_name = 't2' ORDER BY constraint_name;
223+
constraint_name
224+
t2_ibfk_1
225+
# Add FK
226+
ALTER TABLE t2 ADD FOREIGN KEY(b) REFERENCES t1(a);
227+
SELECT constraint_name FROM information_schema.referential_constraints
228+
WHERE table_name = 't2' ORDER BY constraint_name;
229+
constraint_name
230+
t2_ibfk_1
231+
t2_ibfk_2
232+
SELECT constraint_name FROM information_schema.table_constraints
233+
WHERE table_name = 't2' ORDER BY constraint_name;
234+
constraint_name
235+
t2_ibfk_1
236+
t2_ibfk_2
237+
# Remove first FK and add a new FK.
238+
ALTER TABLE t2 DROP FOREIGN KEY t2_ibfk_1;
239+
ALTER TABLE t2 ADD FOREIGN KEY(a) REFERENCES t1(a);
240+
SELECT constraint_name FROM information_schema.referential_constraints
241+
WHERE table_name = 't2' ORDER BY constraint_name;
242+
constraint_name
243+
t2_ibfk_2
244+
t2_ibfk_3
245+
SELECT constraint_name FROM information_schema.table_constraints
246+
WHERE table_name = 't2' ORDER BY constraint_name;
247+
constraint_name
248+
t2_ibfk_2
249+
t2_ibfk_3
250+
# Rename table in different ways.
251+
ALTER TABLE t2 RENAME TO t3;
252+
SELECT constraint_name FROM information_schema.referential_constraints
253+
WHERE table_name = 't3' ORDER BY constraint_name;
254+
constraint_name
255+
t3_ibfk_2
256+
t3_ibfk_3
257+
SELECT constraint_name FROM information_schema.table_constraints
258+
WHERE table_name = 't3' ORDER BY constraint_name;
259+
constraint_name
260+
t3_ibfk_2
261+
t3_ibfk_3
262+
ALTER TABLE t3 RENAME TO t4, ALGORITHM= INPLACE;
263+
SELECT constraint_name FROM information_schema.referential_constraints
264+
WHERE table_name = 't4' ORDER BY constraint_name;
265+
constraint_name
266+
t4_ibfk_2
267+
t4_ibfk_3
268+
SELECT constraint_name FROM information_schema.table_constraints
269+
WHERE table_name = 't4' ORDER BY constraint_name;
270+
constraint_name
271+
t4_ibfk_2
272+
t4_ibfk_3
273+
ALTER TABLE t4 RENAME TO t5;
274+
SELECT constraint_name FROM information_schema.referential_constraints
275+
WHERE table_name = 't5' ORDER BY constraint_name;
276+
constraint_name
277+
t5_ibfk_2
278+
t5_ibfk_3
279+
SELECT constraint_name FROM information_schema.table_constraints
280+
WHERE table_name = 't5' ORDER BY constraint_name;
281+
constraint_name
282+
t5_ibfk_2
283+
t5_ibfk_3
284+
RENAME TABLE t5 TO t6;
285+
SELECT constraint_name FROM information_schema.referential_constraints
286+
WHERE table_name = 't6' ORDER BY constraint_name;
287+
constraint_name
288+
t6_ibfk_2
289+
t6_ibfk_3
290+
SELECT constraint_name FROM information_schema.table_constraints
291+
WHERE table_name = 't6' ORDER BY constraint_name;
292+
constraint_name
293+
t6_ibfk_2
294+
t6_ibfk_3
295+
# Simulate dump+restore and test rename
296+
DROP TABLE t6;
297+
CREATE TABLE `t6` (
298+
`a` int(11) DEFAULT NULL,
299+
`b` int(11) DEFAULT NULL,
300+
KEY `b` (`b`),
301+
KEY `a` (`a`),
302+
CONSTRAINT `t6_ibfk_2` FOREIGN KEY (`b`) REFERENCES `t1` (`a`),
303+
CONSTRAINT `t6_ibfk_3` FOREIGN KEY (`a`) REFERENCES `t1` (`a`)
304+
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
305+
SELECT constraint_name FROM information_schema.referential_constraints
306+
WHERE table_name = 't6' ORDER BY constraint_name;
307+
constraint_name
308+
t6_ibfk_2
309+
t6_ibfk_3
310+
SELECT constraint_name FROM information_schema.table_constraints
311+
WHERE table_name = 't6' ORDER BY constraint_name;
312+
constraint_name
313+
t6_ibfk_2
314+
t6_ibfk_3
315+
RENAME TABLE t6 TO t2;
316+
SELECT constraint_name FROM information_schema.referential_constraints
317+
WHERE table_name = 't2' ORDER BY constraint_name;
318+
constraint_name
319+
t2_ibfk_2
320+
t2_ibfk_3
321+
SELECT constraint_name FROM information_schema.table_constraints
322+
WHERE table_name = 't2' ORDER BY constraint_name;
323+
constraint_name
324+
t2_ibfk_2
325+
t2_ibfk_3
326+
# Remove all FKs and add one back
327+
ALTER TABLE t2 DROP FOREIGN KEY t2_ibfk_2, DROP FOREIGN KEY t2_ibfk_3;
328+
ALTER TABLE t2 ADD FOREIGN KEY(a) REFERENCES t1(a);
329+
SELECT constraint_name FROM information_schema.referential_constraints
330+
WHERE table_name = 't2' ORDER BY constraint_name;
331+
constraint_name
332+
t2_ibfk_1
333+
SELECT constraint_name FROM information_schema.table_constraints
334+
WHERE table_name = 't2' ORDER BY constraint_name;
335+
constraint_name
336+
t2_ibfk_1
337+
# Add a foreign key with close to generated name
338+
ALTER TABLE t2 ADD CONSTRAINT t3_ibfk_2 FOREIGN KEY(b) REFERENCES t1(a);
339+
SELECT constraint_name FROM information_schema.referential_constraints
340+
WHERE table_name = 't2' ORDER BY constraint_name;
341+
constraint_name
342+
t2_ibfk_1
343+
t3_ibfk_2
344+
SELECT constraint_name FROM information_schema.table_constraints
345+
WHERE table_name = 't2' ORDER BY constraint_name;
346+
constraint_name
347+
t2_ibfk_1
348+
t3_ibfk_2
349+
# Then rename so that the given name now matches a generated name
350+
RENAME TABLE t2 TO t3;
351+
SELECT constraint_name FROM information_schema.referential_constraints
352+
WHERE table_name = 't3' ORDER BY constraint_name;
353+
constraint_name
354+
t3_ibfk_1
355+
t3_ibfk_2
356+
SELECT constraint_name FROM information_schema.table_constraints
357+
WHERE table_name = 't3' ORDER BY constraint_name;
358+
constraint_name
359+
t3_ibfk_1
360+
t3_ibfk_2
361+
# Finally rename it again. The given name is now seen as generated and renamed.
362+
RENAME TABLE t3 TO t4;
363+
SELECT constraint_name FROM information_schema.referential_constraints
364+
WHERE table_name = 't4' ORDER BY constraint_name;
365+
constraint_name
366+
t4_ibfk_1
367+
t4_ibfk_2
368+
SELECT constraint_name FROM information_schema.table_constraints
369+
WHERE table_name = 't4' ORDER BY constraint_name;
370+
constraint_name
371+
t4_ibfk_1
372+
t4_ibfk_2
373+
DROP TABLE t4;
374+
# Make a foreign key with given name matching a generated name
375+
CREATE TABLE t2(a INT, b INT);
376+
ALTER TABLE t2 ADD CONSTRAINT t2_ibfk_1 FOREIGN KEY(a) REFERENCES t1(a);
377+
ALTER TABLE t2 ADD FOREIGN KEY(b) REFERENCES t1(a);
378+
SELECT constraint_name FROM information_schema.referential_constraints
379+
WHERE table_name = 't2' ORDER BY constraint_name;
380+
constraint_name
381+
t2_ibfk_1
382+
t2_ibfk_2
383+
SELECT constraint_name FROM information_schema.table_constraints
384+
WHERE table_name = 't2' ORDER BY constraint_name;
385+
constraint_name
386+
t2_ibfk_1
387+
t2_ibfk_2
388+
DROP TABLE t2;
389+
# Test FK name case sensitivity
390+
CREATE TABLE t2(a INT, b INT);
391+
ALTER TABLE t2 ADD CONSTRAINT FK FOREIGN KEY(a) REFERENCES t1(a);
392+
SELECT constraint_name FROM information_schema.referential_constraints
393+
WHERE table_name = 't2' ORDER BY constraint_name;
394+
constraint_name
395+
FK
396+
SELECT constraint_name FROM information_schema.table_constraints
397+
WHERE table_name = 't2' ORDER BY constraint_name;
398+
constraint_name
399+
FK
400+
ALTER TABLE t2 ADD CONSTRAINT fk FOREIGN KEY(b) REFERENCES t1(a);
401+
ERROR 42000: Duplicate key name 'fk'
402+
ALTER TABLE t2 DROP FOREIGN KEY FK;
403+
# Name matching generated name, but different case.
404+
ALTER TABLE t2 ADD CONSTRAINT T2_IBFK_1 FOREIGN KEY(a) REFERENCES t1(a);
405+
ALTER TABLE t2 ADD FOREIGN KEY(b) REFERENCES t1(a);
406+
ERROR HY000: Duplicate foreign key constraint name 't2_ibfk_1'
407+
ALTER TABLE t2 DROP FOREIGN KEY T2_IBFK_1;
408+
DROP TABLE t2;
409+
# Check long FK generated names due to long table names.
410+
CREATE TABLE t2 (a INT, FOREIGN KEY (a) REFERENCES t1(a));
411+
RENAME TABLE t2 TO t123456789012345678901234567890123456789012345678901234567;
412+
ERROR 42000: Identifier name 't123456789012345678901234567890123456789012345678901234567_ibfk_1' is too long
413+
RENAME TABLE t2 TO t12345678901234567890123456789012345678901234567890123456;
414+
SELECT constraint_name FROM information_schema.referential_constraints
415+
WHERE table_name = 't12345678901234567890123456789012345678901234567890123456'
416+
ORDER BY constraint_name;
417+
constraint_name
418+
t12345678901234567890123456789012345678901234567890123456_ibfk_1
419+
SELECT constraint_name FROM information_schema.table_constraints
420+
WHERE table_name = 't12345678901234567890123456789012345678901234567890123456'
421+
ORDER BY constraint_name;
422+
constraint_name
423+
t12345678901234567890123456789012345678901234567890123456_ibfk_1
424+
DROP TABLE t12345678901234567890123456789012345678901234567890123456;
425+
CREATE TABLE t123456789012345678901234567890123456789012345678901234567(
426+
a INT, FOREIGN KEY (a) REFERENCES t1(a));
427+
ERROR 42000: Identifier name 't123456789012345678901234567890123456789012345678901234567_ibfk_1' is too long
428+
CREATE TABLE t123456789012345678901234567890123456789012345678901234567890123(
429+
a INT, CONSTRAINT fk FOREIGN KEY (a) REFERENCES t1(a));
430+
DROP TABLE t123456789012345678901234567890123456789012345678901234567890123;
431+
DROP TABLE t1;
432+
# FK Referencing virtual column
433+
CREATE TABLE t1(a INT PRIMARY KEY,
434+
b INT GENERATED ALWAYS AS (a+1) VIRTUAL UNIQUE);
435+
CREATE TABLE t2(a INT, FOREIGN KEY (a) REFERENCES t1(b));
436+
ERROR HY000: Cannot add foreign key constraint
437+
CREATE TABLE t2(a INT);
438+
ALTER TABLE t2 ADD FOREIGN KEY(a) REFERENCES t1(b);
439+
ERROR HY000: Cannot add foreign key constraint
440+
DROP TABLE t1, t2;
441+
# FK on generated stored column
442+
CREATE TABLE t1(a INT PRIMARY KEY);
443+
CREATE TABLE t2(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE);
444+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
445+
FOREIGN KEY (b) REFERENCES t1(a));
446+
ALTER TABLE t2 ADD FOREIGN KEY(b) REFERENCES t1(a);
447+
ALTER TABLE t2 DROP FOREIGN KEY t2_ibfk_1;
448+
DROP TABLE t3;
449+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
450+
FOREIGN KEY (b) REFERENCES t1(a) ON UPDATE CASCADE);
451+
ERROR HY000: Cannot define foreign key with ON UPDATE CASCADE clause on a generated column.
452+
ALTER TABLE t2 ADD FOREIGN KEY(b) REFERENCES t1(a) ON UPDATE CASCADE;
453+
ERROR HY000: Cannot define foreign key with ON UPDATE CASCADE clause on a generated column.
454+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
455+
FOREIGN KEY (b) REFERENCES t1(a) ON DELETE SET NULL);
456+
ERROR HY000: Cannot define foreign key with ON DELETE SET NULL clause on a generated column.
457+
ALTER TABLE t2 ADD FOREIGN KEY(b) REFERENCES t1(a) ON DELETE SET NULL;
458+
ERROR HY000: Cannot define foreign key with ON DELETE SET NULL clause on a generated column.
459+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
460+
FOREIGN KEY (b) REFERENCES t1(a) ON UPDATE SET NULL);
461+
ERROR HY000: Cannot define foreign key with ON UPDATE SET NULL clause on a generated column.
462+
ALTER TABLE t2 ADD FOREIGN KEY(b) REFERENCES t1(a) ON UPDATE SET NULL;
463+
ERROR HY000: Cannot define foreign key with ON UPDATE SET NULL clause on a generated column.
464+
# FK on Base column of generated stored column.
465+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
466+
FOREIGN KEY (a) REFERENCES t1(a));
467+
ALTER TABLE t2 ADD FOREIGN KEY(a) REFERENCES t1(a);
468+
ALTER TABLE t2 DROP FOREIGN KEY t2_ibfk_1;
469+
DROP TABLE t3;
470+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
471+
FOREIGN KEY (a) REFERENCES t1(a) ON UPDATE CASCADE);
472+
ERROR HY000: Cannot add foreign key constraint
473+
ALTER TABLE t2 ADD FOREIGN KEY(a) REFERENCES t1(a) ON UPDATE CASCADE;
474+
ERROR HY000: Cannot add foreign key constraint
475+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
476+
FOREIGN KEY (a) REFERENCES t1(a) ON DELETE SET NULL);
477+
ERROR HY000: Cannot add foreign key constraint
478+
ALTER TABLE t2 ADD FOREIGN KEY(a) REFERENCES t1(a) ON DELETE SET NULL;
479+
ERROR HY000: Cannot add foreign key constraint
480+
CREATE TABLE t3(a INT, b INT GENERATED ALWAYS AS (a+1) STORED UNIQUE,
481+
FOREIGN KEY (a) REFERENCES t1(a) ON UPDATE SET NULL);
482+
ERROR HY000: Cannot add foreign key constraint
483+
ALTER TABLE t2 ADD FOREIGN KEY(a) REFERENCES t1(a) ON UPDATE SET NULL;
484+
ERROR HY000: Cannot add foreign key constraint
485+
DROP TABLE t2, t1;
486+
# FK on virtual column not supported.
487+
CREATE TABLE t1(a INT PRIMARY KEY);
488+
CREATE TABLE t2(a INT, b INT GENERATED ALWAYS AS (a+1) VIRTUAL UNIQUE,
489+
FOREIGN KEY(b) REFERENCES t1(a));
490+
ERROR HY000: Cannot add foreign key constraint
491+
CREATE TABLE t2(a INT, b INT GENERATED ALWAYS AS (a+1) VIRTUAL UNIQUE);
492+
ALTER TABLE t2 ADD FOREIGN KEY (b) REFERENCES t1(a);
493+
ERROR HY000: Cannot add foreign key constraint
494+
DROP TABLE t2;
495+
CREATE TABLE t2(a INT, b INT, FOREIGN KEY(b) REFERENCES t1(a));
496+
ALTER TABLE t2 MODIFY COLUMN b INT GENERATED ALWAYS AS (a+1) VIRTUAL;
497+
ERROR HY000: 'Changing the STORED status' is not supported for generated columns.
498+
DROP TABLE t2, t1;
499+
# Trying to drop columns used in multi-column FKs.
500+
CREATE TABLE t1(a INT PRIMARY KEY, b INT, INDEX(a, b));
501+
CREATE TABLE t2(a INT, b INT, FOREIGN KEY(a, b) REFERENCES t1(a, b));
502+
ALTER TABLE t2 DROP COLUMN a;
503+
ERROR HY000: Cannot drop column 'a': needed in a foreign key constraint 't2_ibfk_1'
504+
ALTER TABLE t2 DROP COLUMN b;
505+
ERROR HY000: Cannot drop column 'b': needed in a foreign key constraint 't2_ibfk_1'
506+
DROP TABLE t2;
507+
# Use explicitly named index to check where index name is != column name.
508+
CREATE TABLE t2(a INT, b INT, INDEX idx(a, b),
509+
FOREIGN KEY(a, b) REFERENCES t1(a, b));
510+
ALTER TABLE t2 DROP COLUMN a;
511+
ERROR HY000: Cannot drop column 'a': needed in a foreign key constraint 't2_ibfk_1'
512+
ALTER TABLE t2 DROP COLUMN b;
513+
ERROR HY000: Cannot drop column 'b': needed in a foreign key constraint 't2_ibfk_1'
514+
DROP TABLE t2, t1;
515+
# Index with prefix cannot be used for supporting FK.
516+
CREATE TABLE t1 (PK VARCHAR(100) PRIMARY KEY);
517+
CREATE TABLE t2 (FK VARCHAR(100), FOREIGN KEY(FK) REFERENCES t1 (PK), KEY(FK));
518+
ALTER TABLE t2 DROP INDEX FK, ADD INDEX FK2(FK(10));
519+
ERROR HY000: Cannot drop index 'FK': needed in a foreign key constraint
520+
DROP TABLE t2, t1;
521+
SET @@foreign_key_checks= DEFAULT;

0 commit comments

Comments
 (0)