I am drawn to this design like a moth to a flame. I've read rumblings that cyclic foreign keys are a nightmare. Generally, I can see why they should be avoided. In this particular case, however, I don't see why it would be so awful. Got some inspiration from this article, but combined the reverse-belongs-to and exclusive-belongs-to designs into one... monstrous creation?
Can you tell me why (or if) this is a bad idea? Like what headaches am I going to get into with this and are they worth it?
Anyway, I want to create a polymorphic relationship between many entities and one table. I want to make something like this:
-- poly ids are uuids and we assume they will not collide, periodcreate domain poly_id uuid;create table foo ([...table columns], poly_id not null default gen_random_uuid())create table bar ([...table columns], poly_id not null default gen_random_uuid())create table baz ([...table columns], poly_id not null default gen_random_uuid())create type poly_t as enum ('foo', 'bar', 'baz')create table poly_obj ( -- poly_id is always the poly_id of the one set reference column poly_id poly_id not null generated always as ( coalesce("foo", "bar", "baz") ), poly_t poly_t not null,"foo" poly_id null references foo (poly_id) check ( "foo" is null or poly_t = 'foo' ),"bar" poly_id null references bar (poly_id) check ( "bar" is null or poly_t = 'bar' ),"baz" poly_id null references baz (poly_id) check ( "baz" is null or poly_t = 'baz' ) -- only one fk to child table can be set check ( ( ("foo" is not null)::integer + ("bar" is not null)::integer + ("baz" is not null)::integer ) = 1 ))create unique index on poly_obj ("foo") where "foo" is not null;create unique index on poly_obj ("bar") where "bar" is not null;create unique index on poly_obj ("baz") where "baz" is not null;alter table foo add foreign key (poly_id) references poly_obj (poly_id)alter table bar add foreign key (poly_id) references poly_obj (poly_id)alter table baz add foreign key (poly_id) references poly_obj (poly_id)-- more pseudocodey than the restcreate trigger ___ after insert on foo for each row execute insert into poly_obj (poly_t, foo) select ('foo', poly_id) from new;create trigger ___ after insert on bar for each row execute insert into poly_obj (poly_t, bar) select ('bar', poly_id) from new;create trigger ___ after insert on baz for each row execute insert into poly_obj (poly_t, baz) select ('baz', poly_id) from new;