Bug #121444 Composite self-referencing FOREIGN KEY not enforced: a dangling row with both key columns set is accepted
Submitted: 7 Oct 5:00
Reporter: Haikuo Jiang Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S2 (Serious)
Version:26.7.0 OS:Any
Assigned to: CPU Architecture:Any
Tags: composite key, constraint not enforced, foreign key, innodb_native_foreign_keys, self-referencing

[7 Oct 5:00] Haikuo Jiang
Description:
Expected:

  ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails

Actual: the INSERT succeeds and this row is stored, although no row with key (5, 777) exists.

  k1=5  k2=100  f1=5  f2=777

Both foreign key columns are non-NULL, so the documented MATCH SIMPLE case - which permits a key that
is all or partially NULL - does not apply.

  innodb_native_foreign_keys=OFF (default, SQL layer):  accepted, the row is stored
  innodb_native_foreign_keys=ON  (InnoDB):              ERROR 1452
  MySQL 8.0.28:                                         ERROR 1452

Trigger, all three together; change any one and the constraint is enforced again:

  1. the foreign key is composite AND self-referencing;
  2. the table is empty when the row is inserted;
  3. the new row's foreign key leading column equals its own key leading column (f1 = k1).

The catalogue still reports the constraint and no DDL statement fails, so no catalogue-reading check
can see this; only an oracle that tries to violate the constraint can.

How to repeat:
1. Run the SQL below on 26.7.0 with default settings.
2. Observe that the first INSERT is accepted and the row is stored, although (5, 777) exists nowhere.
3. The control INSERT differs only in f1 and is refused with ERROR 1452.
4. Restart with --innodb_native_foreign_keys=ON and run the first INSERT again: now ERROR 1452.

  DROP DATABASE IF EXISTS bug2;
  CREATE DATABASE bug2;
  USE bug2;

  CREATE TABLE t (k1 BIGINT NOT NULL, k2 BIGINT NOT NULL,
    f1 BIGINT NULL, f2 BIGINT NULL,
    PRIMARY KEY (k1, k2),
    CONSTRAINT fk_t FOREIGN KEY (f1, f2) REFERENCES t (k1, k2)) ENGINE=InnoDB;

  -- the table is empty; no row with key (5, 777) exists
  INSERT INTO t (k1, k2, f1, f2) VALUES (5, 100, 5, 777);
  -- accepted on the default SQL-layer path

  -- control: same shape, f1 differs from the row's own k1
  DELETE FROM t;
  INSERT INTO t (k1, k2, f1, f2) VALUES (5, 100, 6, 777);
  -- ERROR 1452