Programmering

MySQL på nye fremmednøkkeleventyr

13. oktober 2009 · Ingen Kommentarer

Ikke så lenge siden skrev jeg om MySQL sine problemer med datatyper og NULL-konstraints. Og problemene stopper ikke der.

Jeg kom i dag over en merkelig og helt uforståelig bug i MySQL. Eller, det heter vel kanskje feature når det gjelder MySQL.

Ta følgende scenario: En kolonne “disabled_reason_ref” i tabellen “countries” refererer til feltet “uuid” i tabellen “texts”. Kolonnen “disabled_reason_ref” er definert med mindre størrelse enn feltet den refererer til. Dette visste jeg ikke om. Begge tabellene bruker InnoDB og det er satt opp fremmednøkler. Hvordan dette gir noe som helst mening i MySQL-verden vet jeg ikke.

Ihvertfall. Det hele startet med denne SQL INSERT-spørringen.


mysql> INSERT INTO countries (uuid, country_code, disabled, disabled_reason_ref, page_ref) VALUES ("a63d5a26-9719-47dd-9617-c07bd8126061", "NO", 1, "e80c8d7c-dd84-4a4f-8df4-778efe9b311a", NULL);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`utenlandsbolig/countries`, CONSTRAINT `countries_ibfk_2` FOREIGN KEY (`disabled_reason_ref`) REFERENCES `texts` (`uuid`))

Det er ingenting i feilmeldingen fra denne spørringen som sier noe om det.

Jeg lurte litt på om kolonnenavnet var for langt, så jeg lagde en ny kolonne som heter “reason_ref”.


mysql> alter table countries add column  reason_ref VARCHAR(32)  REFERENCES texts(uuid);
Query OK, 1 row affected (0.52 sec)
Records: 1  Duplicates: 0  Warnings: 0

MySQL satte riktignok ikke opp noen fremmednøkkel her, noe jeg sjekket med “SHOW CREATE TABLE countries”.
Lurer da fælt på hva den i så fall referer til…
Vel, MySQL er MySQL.

Jeg tenkte så at jeg ikke trengte “disabled_reason_ref” lenger.


mysql> alter table countries drop column disabled_reason_ref;
ERROR 1025 (HY000): Error on rename of './utenlandsbolig/#sql-4fa3_6' to './utenlandsbolig/countries' (errno: 150)

Hva i alle dager betyr dette?
Jeg kom meg ikke rundt feilen, så jeg lot kolonnen bare ligge.

Jeg hadde på dette tidspunktet ikke oppdaget at “reason_ref” var for liten. Kolonnen den refererer til er nemlig av type VARCHAR(36).

Et nytt forsøk på INSERT, nå ved å bruke den nye kolonnen.


mysql> INSERT INTO countries (uuid, country_code, disabled, reason_ref, page_ref) VALUES ("a63d5a26-9719-47dd-9617-c07bd8126061", "NO", 1, "e80c8d7c-dd84-4a4f-8df4-778efe9b311a", NULL);
Query OK, 1 row affected, 1 warning (0.19 sec)

mysql> show warnings;
+---------+------+-------------------------------------------------+
| Level   | Code | Message                                         |
+---------+------+-------------------------------------------------+
| Warning | 1265 | Data truncated for column 'reason_ref' at row 1 |
+---------+------+-------------------------------------------------+
1 row in set (0.00 sec)

Og det viste seg at kolonnen var for liten. Dermed fant den ingen rad i “texts”-tabellen som hadde den refererte verdien på 32 tegn, da verdien egentlig skulle vært på 36 tegn.


mysql> alter table countries modify column disabled_reason_ref varchar(36);
Query OK, 2 rows affected (0.40 sec)
Records: 2  Duplicates: 0  Warnings: 0

Greit at det var jeg som gjorde feilen med å deklarere kolonnen for liten.
Men hallooo, litt hjelp kan man vel få fra en database? Spesielt fra en relasjonsdatabase!

Dumme MySQL!