1

My table permission looks like:

id serial,
person_id integer,
permission_id integer,
valid_from date,
valid_to date

I'd like to prevent creating permissions which overlaps valid_from, valid_to date

eg.

1 | 1 | 1 | 2010-10-01 | 2999-12-31
2 | 1 | 2 | 2010-10-01 | 2020-12-31
3 | 2 | 1 | 2015-10-01 | 2999-12-31

this can be added:

4 | 1 | 3 | 2011-10-01 | 2999-12-31 - because no such permission
5 | 2 | 1 | 2011-10-10 | 2999-12-31 - because no such person
6 | 1 | 2 | 2021-01-01 | 2999-12-31 - because doesn't overlaps id:2

but this can't

7 | 1 | 1 | 2009-10-01 | 2010-02-01 - because overlaps id:1
8 | 1 | 2 | 2019-01-01 | 2022-12-31 - because overlaps id:2
9 | 2 | 1 | 2010-01-01 | 2016-12-31 - beacuse overlaps id:3

I can do outside checking but wonder if possible to do it on database

1 Answer 1

3

A unique constraint is based on an equality operator and cannot be used in this case, but you can use an exclude constraint. The constraint uses btree operators <> and =, hence you have to install btree_gist extension.

create extension if not exists btree_gist;

create table permission(
    id serial,
    person_id integer,
    permission_id integer,
    valid_from date,
    valid_to date,
    exclude using gist (
        person_id with =, 
        permission_id with =, 
        daterange(valid_from, valid_to) with &&)
);

These inserts are successful:

insert into permission values
    (1, 1, 1, '2010-10-01', '2999-12-31'),
    (2, 1, 2, '2010-10-01', '2020-12-31'),
    (3, 2, 1, '2015-10-01', '2999-12-31'),
    (4, 1, 3, '2011-10-01', '2999-12-31'),
    (5, 3, 1, '2011-10-10', '2999-12-31'), -- you meant person_id = 3 I suppose
    (6, 1, 2, '2021-01-01', '2999-12-31'),
    (7, 1, 1, '2009-10-01', '2010-02-01'); -- ranges do not overlap!

but this one is not:

insert into permission values
    (8, 1, 2, '2019-01-01', '2022-12-31');

ERROR:  conflicting key value violates exclusion constraint "permission_person_id_permission_id_daterange_excl"
DETAIL:  Key (person_id, permission_id, daterange(valid_from, valid_to))=(1, 2, [2019-01-01,2022-12-31)) conflicts with existing key (person_id, permission_id, daterange(valid_from, valid_to))=(1, 2, [2010-10-01,2020-12-31)).

Try it in db<>fiddle.

Sign up to request clarification or add additional context in comments.

2 Comments

Perfect solution! Now I have a time to do a tests - it's exactly what I need. I suppose, that I should do create extension if not exists btree_gist; for each database once?
Yes, the extension should be installed once in a database you need it in.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.