2011-07-19 48 views
8

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

例如,我有三个表:

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

我想创建它验证用户始终是一个组的一部分的触发器。不允许孤儿用户。每次发生用户插入时,此触发器都会验证是否也发生了到user_in_group中的相应插入操作。如果不是,交易将不会提交。

这不能使用简单的基于行或语句的触发器来完成,因为上述场景需要两个单独的语句。

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

回答

0

the docs。 。 。

触发器可以定义之前或任何INSERT之后执行, UPDATE或DELETE操作,每修改行任一一次,或每 SQL语句一次。

+0

我知道。使用这些类型的触发器无法完成问题中描述的场景。这就是我寻找间接方式的原因。你碰巧知道吗? –

1

Lookind的文档,似乎没有这样的触发选项...这样一个实现方式“无孤儿用户”的规则将不允许直接INSERT INTO usersuser_in_group表。相反,创建一个视图(它将这些表,即user_id, user_name, group_id)与一个update rule,它将数据插入到右表中。

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

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

+0

查看更新规则是很酷的想法,谢谢!关于你的其他建议,这是如何工作的?在程序之外执行查询有什么区别? –

+0

SP的想法是将两个插入操作合并到一个命令中 - 如果它在任何时候都失败(即,您已经插入'users'表中但未插入到'user_group'中),所有操作都将回滚并且不会结束与孤儿记录。 – ain

+0

啊好的。但通过仅使用SP,我无法阻止用户直接访问表格? –

1

难道真正的方法是建立数据库的约束吗?这似乎恰恰是约束条件。添加一个外键约束和一个非空的,它似乎你应该在业务。

现在修为对称的约束:

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

的(不可延迟,BOO)检查功能实例:

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

请再次阅读说明。这是关于插入用户而不插入user_in_group。不是关于从user_in_group中删除或使用无效的引用。 –

+0

@Appelsien S:请再读一遍我的答案。我在添加示例后立即指出,我的约束工作方式是错误的,现在已经修复它,以便孤儿用户不被允许(虽然仍然禁止糟糕的user_in_group条目 - 不禁止这些会更简单,因为没有创建表更改表将是必需的)。 –

+0

在修订后的版本中,foousergrps中的user_id和group_id是唯一的。这当然不是理想的:foousergrps是一个N对N的关系。你做到了1-1的关系。 –

15

你看进CREATE CONSTRAINT TRIGGER,与DEFERRABLE (INITIALLY DEFERRED)选项?

+0

+1,以及对外键,唯一键和排除约束使用相同的“可延迟初始延迟”。 –

+0

谢谢。我没有意识到可以在约束触发器上设置DEFERRABLE。这最好地解决了我的问题。 –

+1

如果他们有这个工作,为什么他们不允许CHECK()约束延迟?那么,好的答案。 –

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)