Re: Protect a table against concurrent data changes while allowing to vacuum it

Поиск
Список
Период
Сортировка
От Albe Laurenz
Тема Re: Protect a table against concurrent data changes while allowing to vacuum it
Дата
Msg-id A737B7A37273E048B164557ADEF4A58B53863C5E@ntex2010i.host.magwien.gv.at
обсуждение исходный текст
Ответ на Re: Protect a table against concurrent data changes while allowing to vacuum it  (Sameer Kumar <sameer.kumar@ashnik.com>)
Ответы Re: Protect a table against concurrent data changes while allowing to vacuum it  (Vlad Arkhipov <arhipov@dc.baikal.ru>)
Re: Protect a table against concurrent data changes while allowing to vacuum it  (Jehan-Guillaume de Rorthais <ioguix@free.fr>)
Список pgsql-general
Sameer Kumar wrote:
> On Wed, Jun 22, 2016 at 6:08 PM Vlad Arkhipov <arhipov@dc.baikal.ru> wrote:
>> I am running PostgreSQL 9.5.
>> 
>> CREATE TABLE t (id BIGINT NOT NULL PRIMARY KEY, name TEXT);
>> 
>> The constraint that the data must satisfy is `there is no more than 3 records with the same name`.
>> 
>> I am not in control of queries that modify the table, so advisory locks can hardly be of help to me.
> 
> 
> Define a function which does a count of the rows and if count is 3 it return false if count is less it
> returns true.
> 
> Use check constraint with this function. I have not tried this so not sure if you can use function
> with SELECT on same table in CHECK constraint. So test it out first.
> 
> If this works, any insert trying to get the 4th record in table would fail.

You cannot use subqueries in a check constraint:

ALTER TABLE t
   ADD CONSTRAINT name_count
      CHECK ((SELECT count(*) FROM t t1 WHERE t1.name = name) <= 3);
ERROR:  cannot use subquery in check constraint

> A last resort could be using triggers. But either of these approaches will cause issues if you have
> high concurrency.

Yes, triggers is the way to go:

CREATE OR REPLACE FUNCTION check_t() RETURNS trigger LANGUAGE plpgsql AS
$$BEGIN
   IF (SELECT count(*) FROM t WHERE name = NEW.name) > 3 THEN
      RAISE EXCEPTION 'More than three values!';
   END IF;
   RETURN NEW;
END;$$;

CREATE TRIGGER check_t AFTER INSERT OR UPDATE ON t FOR EACH ROW
   EXECUTE PROCEDURE check_t();

But be warned that this will only work if all transactions involved use
the isolation level SERIALIZABLE.

Otherwise two concurrent INSERTs would not see each other's entry, and the
triggers would not raise an error even if there are more than three entries
after COMMIT.

Yours,
Laurenz Albe

В списке pgsql-general по дате отправления:

Предыдущее
От: Sameer Kumar
Дата:
Сообщение: Re: Protect a table against concurrent data changes while allowing to vacuum it
Следующее
От: Vlad Arkhipov
Дата:
Сообщение: Re: Protect a table against concurrent data changes while allowing to vacuum it