postgres - 在事务提交之前触发

时间:2011-07-19 12:01:12

标签: postgresql transactions triggers

我想知道在事务即将提交之前是否可以间接执行触发器?在此触发器中,我将进行一致性检查并在需要时回滚事务。

例如,我有三个表:

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

我想创建一个触发器,用于验证用户是否始终是组的一部分。不允许孤儿用户。每次插入用户时,此触发器将验证是否还发生了进入user_in_group的相应插入。如果没有,交易将不会提交。

使用简单的基于行或语句的触发器无法完成此操作,因为上述方案需要两个单独的语句。

另一种方法是,当发生从user_in_group的删除时,可以通过基于行的触发器轻松完成。

5 个答案:

答案 0 :(得分:18)

您是否使用DEFERRABLE (INITIALLY DEFERRED)选项查看了CREATE CONSTRAINT TRIGGER

答案 1 :(得分:2)

真正的方法不是真的要在数据库中建立约束吗?这似乎正是约束的目的。添加一个外键约束和一个非null,看起来你应该在业务中。

现在针对对称约束进行了修订:

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));

答案 2 :(得分:1)

看看文档,似乎没有这样的触发选项......所以实现“无孤儿用户”规则的一种方法是不允许直接插入usersuser_in_group表。而是创建一个视图(将这些表组合,即user_id, user_name, group_id)与update rule,将数据插入到右表中。

或者只允许通过存储过程插入新用户,该存储过程将所有必需数据作为inpud,因此不允许用户使用组。

顺便说一句,为什么你有单独的用户和组关系表?为什么不将group_id字段添加到具有users约束的FK / NOT NULL表中?

答案 3 :(得分:0)

来自the docs。 。

  

触发器可以定义为在任何INSERT之前或之后执行,   更新或删除操作,每个修改行一次,或每次一次   SQL语句。

答案 4 :(得分:0)

您可以使用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)