Adam Bartosik wrote:
>> MySQL 4.x and 5.0 will accept the above SQL syntax, but will silently
>> ignore it, and not actually perform any referential integrity checking,
>> nor cascading. ie. It all looks like it's working fine, until it breaks
>> horribly when you get into detailed testing. You have been warned.
> 
> Please, please RTFM before writing such things, eg:
> http://dev.mysql.com/tech-resources/articles/mysql-enforcing-foreign-keys.html
> It is better to know how stuff works - there are different formats of
> tables (storages) in MySQL so you can use what is better for your
> needs and defaults are not always the best. It just like flexibility
> in Catalyst compared to ... Rails?

Attached are two SQL files. One of them uses "default" MySQL tables, the
other one creates them as InnoDB tables.
I have run them against MySQL 5 and in both cases, the foreign key
constraints and cascading deletes are silently ignored. How is that
"better for your needs"?
You're welcome to verify this yourself. I include the captured output
too, although it'll make more sense if you look at the SQL first.
I also include the output from running the normal SQL through Postgres
8, and you can see the difference - the cascading delete occurs, and an
error is thrown when an invalid foreign key is inserted.

Note: I am aware that there are workarounds available to make MySQL
overcome these limitations - however, I stand by the belief that it is
harmful that MySQL silently ignores the standard SQL syntax.

-Toby
CREATE TABLE accounts (
    id INTEGER PRIMARY KEY,
    username VARCHAR(32)
);
CREATE TABLE roles (
    role VARCHAR(32) PRIMARY KEY
);
CREATE TABLE accounts_to_roles (
    account INTEGER NOT NULL REFERENCES accounts(id)
            ON DELETE CASCADE,
    role    VARCHAR(32) NOT NULL REFERENCES roles(role)
            ON DELETE CASCADE,
    PRIMARY KEY(account,role)
);

BEGIN;
INSERT INTO accounts(id, username) VALUES (1, 'Toby');
INSERT INTO accounts(id, username) VALUES (2, 'Bob');
INSERT INTO roles(role) VALUES ('Admin');
INSERT INTO roles(role) VALUES ('Luser');
COMMIT;

BEGIN;
-- Test cascading delete:
INSERT INTO accounts_to_roles (account, role) VALUES (2, 'Luser');
DELETE FROM accounts WHERE id=2;
SELECT * FROM accounts_to_roles;
COMMIT;

BEGIN;
-- Test foreign key constraint:
INSERT INTO accounts_to_roles(account, role) VALUES (1, 'Admin');
INSERT INTO accounts_to_roles(account, role) VALUES (1, 'Fake');
INSERT INTO accounts_to_roles(account, role) VALUES (3, 'Unreal');
SELECT * FROM accounts_to_roles;
COMMIT;

CREATE TABLE accounts (
    id INTEGER PRIMARY KEY,
    username VARCHAR(32)
) ENGINE=InnoDB;
CREATE TABLE roles (
    role VARCHAR(32) PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE accounts_to_roles (
    account INTEGER NOT NULL REFERENCES accounts(id)
            ON DELETE CASCADE,
    role    VARCHAR(32) NOT NULL REFERENCES roles(role)
            ON DELETE CASCADE,
    PRIMARY KEY(account,role)
) ENGINE=InnoDB;

BEGIN;
INSERT INTO accounts(id, username) VALUES (1, 'Toby');
INSERT INTO accounts(id, username) VALUES (2, 'Bob');
INSERT INTO roles(role) VALUES ('Admin');
INSERT INTO roles(role) VALUES ('Luser');
COMMIT;

BEGIN;
-- Test cascading delete:
INSERT INTO accounts_to_roles (account, role) VALUES (2, 'Luser');
DELETE FROM accounts WHERE id=2;
SELECT * FROM accounts_to_roles;
COMMIT;

BEGIN;
-- Test foreign key constraint:
INSERT INTO accounts_to_roles(account, role) VALUES (1, 'Admin');
INSERT INTO accounts_to_roles(account, role) VALUES (1, 'Fake');
INSERT INTO accounts_to_roles(account, role) VALUES (3, 'Unreal');
SELECT * FROM accounts_to_roles;
COMMIT;

mysql> \. test.sql
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.02 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

+---------+-------+
| account | role  |
+---------+-------+
|       2 | Luser | 
+---------+-------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.01 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

+---------+--------+
| account | role   |
+---------+--------+
|       1 | Admin  | 
|       1 | Fake   | 
|       2 | Luser  | 
|       3 | Unreal | 
+---------+--------+
4 rows in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> 
mysql> \. test-innodb.sql
Query OK, 0 rows affected (0.04 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

+---------+-------+
| account | role  |
+---------+-------+
|       2 | Luser | 
+---------+-------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

+---------+--------+
| account | role   |
+---------+--------+
|       1 | Admin  | 
|       1 | Fake   | 
|       2 | Luser  | 
|       3 | Unreal | 
+---------+--------+
4 rows in set (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

mysql> 
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "accounts_pkey" 
for table "accounts"
CREATE TABLE
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "roles_pkey" for 
table "roles"
CREATE TABLE
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index 
"accounts_to_roles_pkey" for table "accounts_to_roles"
CREATE TABLE
BEGIN
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
COMMIT
BEGIN
INSERT 0 1
DELETE 1
 account | role 
---------+------
(0 rows)

COMMIT
BEGIN
INSERT 0 1
ERROR:  insert or update on table "accounts_to_roles" violates foreign key 
constraint "accounts_to_roles_role_fkey"
DETAIL:  Key (role)=(Fake) is not present in table "roles".
ERROR:  current transaction is aborted, commands ignored until end of 
transaction block
ERROR:  current transaction is aborted, commands ignored until end of 
transaction block
ROLLBACK
_______________________________________________
List: [email protected]
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/[email protected]/
Dev site: http://dev.catalyst.perl.org/

Reply via email to