Quantcast
Viewing all articles
Browse latest Browse all 60

Modeling Polymorphic Relations in Postgres 15 with bi-directional (cyclic) FK constraints

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;

Viewing all articles
Browse latest Browse all 60

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>