postgres - запуск перед фиксацией транзакции - PullRequest
9 голосов
/ 19 июля 2011

Мне было интересно, возможно ли косвенно выполнить триггер непосредственно перед тем, как транзакция собирается совершить? В этом триггере я буду выполнять проверки непротиворечивости и откатывать транзакцию при необходимости.

Например, у меня есть три таблицы:

users (id, name)
groups (id, name)
user_in_group (user_id, group_id)

Я хотел бы создать триггер, который проверяет, что пользователь всегда является частью группы. Сиротам не разрешено. Каждый раз, когда происходит вставка в пользователей, этот триггер будет проверять, что также произошла соответствующая вставка в user_in_group. Если нет, транзакция не будет зафиксирована.

Это невозможно сделать с помощью простого триггера на основе строк или операторов, поскольку в приведенном выше сценарии требуется два отдельных оператора.

И наоборот, когда происходит удаление из user_in_group, это может быть легко выполнено с помощью триггера на основе строк.

Ответы [ 5 ]

18 голосов
/ 19 июля 2011

Вы смотрели на CREATE CONSTRAINT TRIGGER , с опцией DEFERRABLE (INITIALLY DEFERRED)?

2 голосов
/ 19 июля 2011

Не будет ли правильным методом на самом деле установить ограничения в базе данных?Кажется, именно для этого существуют ограничения.Добавьте ограничение внешнего ключа и ненулевое значение, и, похоже, вы должны быть в бизнесе.

Теперь исправлены симметричные ограничения:

drop table foousers cascade;
drop table foogroups cascade;
drop table foousergrps cascade;
create table foousers (id int primary key, name text);
create table foogroups  (id int primary key, name text);
create table foousergrps (user_id int unique references foousers not null, group_id int unique references foogroups not null);
alter table foogroups add foreign key (id) references foousergrps (group_id) deferrable initially deferred;
alter table foousers add foreign key (id) references foousergrps (user_id) deferrable initially deferred;
begin;
insert into foousers values (0, 'root');
insert into foousers values (1, 'daemon');
insert into foogroups values (0, 'wheel');
insert into foogroups values (1, 'daemon');
insert into foousergrps values (0,0);
insert into foousergrps values (1,1);
commit;

Запрещено:

insert into foousers values (2, 'bad');
insert into foousergrps values (2,2);

Пример функции проверки (не отложенной, бу):

create table foousergrps (user_id int unique references foousers not null, group_id int not null);
create function fooorphangroupcheck(int) returns boolean as $$
declare
  gid alias for $1;
begin
  perform 1 from foousergrps where group_id = gid limit 1;
  if NOT FOUND then return false;
  end if;
  return true;
end;
$$
LANGUAGE 'plpgsql';
alter table foogroups add check (fooorphangroupcheck(id));
1 голос
/ 19 июля 2011

Посмотрите на документ, похоже, что такой опции триггера нет ... поэтому одним из способов достижения правила "нет детей-сирот" было бы запрет прямой вставки в таблицы users и user_in_group. Вместо этого создайте представление (которое объединяет эти таблицы, то есть user_id, user_name, group_id) с правилом обновления , которое вставляет данные в правильные таблицы.

Или разрешить вставку новых пользователей только с помощью хранимой процедуры, которая принимает все необходимые данные как inpud и, таким образом, не позволяет пользователям с большой группой.

Кстати, почему у вас есть отдельная таблица для отношений пользователя и группы? Почему бы не добавить поле group_id в таблицу users с ограничением FK / NOT NULL?

0 голосов
/ 04 апреля 2016

Вы можете использовать оператор sql WITH, например:

WITH insert_user AS (
  INSERT INTO users(name) VALUES ('bla-bla-user') RETURNING id
)
INSERT INTO user_in_group(user_id, group_id) 
  SELECT id, 999 FROM insert_user UNION
  SELECT id, 888 FROM insert_user;

SELECT groups (id, name) user_in_group (user_id, group_id)
0 голосов
/ 19 июля 2011

С документы .,.

Триггеры могут быть определены для выполнения либо до, либо после любой операции INSERT, UPDATE или DELETE, либо один раз для измененной строки, либо один раз для оператора SQL.

...