关于postgresql:postgres – 在事务提交之前触发

postgres - trigger before transaction commit

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

例如,我有三个表:

1
2
3
users (id, name)
groups (id, name)
user_in_group (user_id, group_id)

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

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

反过来,当从user_in_group中删除时,可以通过基于行的触发器轻松完成。


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


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

现在修改了对称约束:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;

禁止:

1
2
INSERT INTO foousers VALUES (2, 'bad');
INSERT INTO foousergrps VALUES (2,2);

(不可延迟,嘘)检查功能的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
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));


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

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

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


您可以使用sql WITH运算符,如下所示:

1
2
3
4
5
6
7
8
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)

来自文档。 。 。

Triggers can be defined to execute either before or after any INSERT,
UPDATE, or DELETE operation, either once per modified row, or once per
SQL statement.