3

There are some situations where I would like to set up some constraints in a table that depend on values in another table.

For example, I can define two tables as:

CREATE TABLE foo (
    foo_id serial PRIMARY KEY,
    max_bar integer
);

CREATE TABLE bar ( foo_id integer REFERENCES foo(foo_id), bar_id integer CHECK (bar_id >= 0), PRIMARY KEY (foo_id, bar_id) );

I would like to go a little further to ensure that all bar.bar_id values are lower that the “corresponding” foo.max_bar.

Is there a way I can set up such a constraint?

To go a little further, I’ve found that an EXCLUDE constraint using a GiST index is a great way to set up UNIQUE-like (but more generic) constraints. For example I can define the following tables:

CREATE TABLE foo (
    foo_id integer NOT NULL,
    foo_validity tstzrange NOT NULL,
    EXCLUDE USING gist (foo_id WITH =, foo_validity WITH &&)
);

CREATE TABLE bar ( foo_id integer NOT NULL, bar_validity tstzrange NOT NULL, EXCLUDE USING gist (foo_id WITH =, bar_validity WITH &&) );

Then I would like each row in bar to reference a row in foo with foo.foo_id = bar.foo_id AND foo.foo_validity @> bar.bar_validity. It looks to me like it should be feasible with gist indices, but I can’t find a syntax to declare such a constraint…

user2233709
  • 203
  • 1
  • 7
  • 1
    You cannot reference foo.max_bar in table bar. This would require an ASSERTION which is a generalization of CHECK constraint, but not yet implemented. The only thing that could work now is if you add max_bar in bar as well and modify the FK to include both columns. – ypercubeᵀᴹ Sep 25 '20 at 14:56
  • Thanks @ypercubeᵀᴹ for pointing CREATE ASSERTION; I did not know about that. Looks powerful but complex to implement (especially where performance is needed). Your proposal to add max_bar in bar is an interesting idea, but it looks quite hackish to me, and would make it more complex to change its value in the foo table… – user2233709 Sep 25 '20 at 15:20
  • Thanks @SahapAsci, I’ve not user triggers much, but that looks like a reasonable way to implement such a foreign-key-like constraint. But unless I’m mistaken it requires a trigger in both tables and might not be straightforward to implement for the reference table (especially for my second example). – user2233709 Sep 25 '20 at 15:25
  • Yes, it would require INSERT/UPDATE triggers in the referencing table (bar) and UPDATE/DELETE triggers in the referenced table (foo). – ypercubeᵀᴹ Sep 25 '20 at 15:35
  • You could use a (NOT VALID) CHECK constraint in both tables, each making use of a fake immutable function like outlined in the last chapter of this related answer: https://dba.stackexchange.com/a/124019/3684. It's not safe against concurrent writes, though. (Don't have time to flesh it out ATM.) – Erwin Brandstetter Sep 25 '20 at 22:47
  • Thanks @ErwinBrandstetter for the idea. However, I don’t really understand how I should write a CHECK constraint for the referred table (foo) to prevent removal (or modification) of a tuple that is referred by the other table (bar)… Any hint? – user2233709 Sep 28 '20 at 09:45

0 Answers0