Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a constraint concerning a max number of rows in postgresql?

I think this is a pretty common problem.

I've got a table user(id INT ...) and a table photo(id BIGINT, owner INT). owner is a reference on user(id).

I'd like to add a constraint to the table photo that would prevent more than let's say 10 photos to enter the database for each users.

What's the best way of writing this?

Thx!

like image 471
jeje Avatar asked Nov 16 '09 16:11

jeje


People also ask

What is the limit for in clause in PostgreSQL?

At least I can tell there is a technical limit of 32767 values (=Short. MAX_VALUE) passable to the PostgreSQL backend, using Posgresql's JDBC driver 9.1. Show activity on this post.

How do I use constraints in PostgreSQL?

A check constraint is the most generic constraint type. It allows you to specify that the value in a certain column must satisfy a Boolean (truth-value) expression. For instance, to require positive product prices, you could use: CREATE TABLE products ( product_no integer, name text, price numeric CHECK (price > 0) );

What is xmin and xmax in PostgreSQL?

When a row is created, the value of xmin is set equal to the ID of the transaction that performed the INSERT command, while xmax is not filled in. When a row is deleted, the xmax value of the current version is labeled with the ID of the transaction that performed DELETE.


1 Answers

Quassnoi is right; a trigger would be the best way to achieve this.

Here's the code:

CREATE OR REPLACE FUNCTION enforce_photo_count() RETURNS trigger AS $$ DECLARE     max_photo_count INTEGER := 10;     photo_count INTEGER := 0;     must_check BOOLEAN := false; BEGIN     IF TG_OP = 'INSERT' THEN         must_check := true;     END IF;      IF TG_OP = 'UPDATE' THEN         IF (NEW.owner != OLD.owner) THEN             must_check := true;         END IF;     END IF;      IF must_check THEN         -- prevent concurrent inserts from multiple transactions         LOCK TABLE photos IN EXCLUSIVE MODE;          SELECT INTO photo_count COUNT(*)          FROM photos          WHERE owner = NEW.owner;          IF photo_count >= max_photo_count THEN             RAISE EXCEPTION 'Cannot insert more than % photos for each user.', max_photo_count;         END IF;     END IF;      RETURN NEW; END; $$ LANGUAGE plpgsql;   CREATE TRIGGER enforce_photo_count      BEFORE INSERT OR UPDATE ON photos     FOR EACH ROW EXECUTE PROCEDURE enforce_photo_count(); 

I included table locking in order to avoid situations where two concurrent tansactions would count photos for a user, see that the current count is 1 below the limit, and then both insert, which would cause you to go 1 over the limit. If that's not a concern for you it would be best to remove the locking as it can become a bottleneck with many inserts/updates.

like image 193
Aleksander Kmetec Avatar answered Nov 07 '22 20:11

Aleksander Kmetec