数据库中表示状态机,强制执行状态转换。

5
我有一个有限状态机,表示作业的各个阶段。我需要在Postgres数据库中表示这些状态。我想通过禁止从一种状态到另一种状态的更新来执行代码正确性检查,除非状态机允许这样做。
一种实现目标的简单方法是在事务中对表进行“独占锁定”,检查当前状态和下一个状态,并在更新无效时中止并显示错误信息。
这显然会降低性能,因为我每次状态转移都要锁定“Job”表。
是否有一种通过约束实现相同目标的方法?

1
如果尝试进行不正确的转换,应该发生什么? - Alexey Milogradov
1
同时,请参考以下讨论:https://dev59.com/9mYq5IYBdhLWcg3wkRi_ - Alexey Milogradov
所以,触发器似乎确实是适当的。有没有一种方法可以触发回滚?我认为这样做:create trigger test before update on "Job" for row when (OLD."status" = 'Init' and NEW."status" != 'Queued') execute procedure rollback()但我宁愿不声明一个rollback函数。 - Dacav
@MatheusOl 是的! :) 找到了 https://www.postgresql.org/message-id/flat/525D9505.1030209%40optionshouse.com#525D9505.1030209@optionshouse.com,我还成功地实现了它。我在想是否真的需要为此编写一个函数,如果需要的话,我会去做的。目前第一个回答问题的人将获得蛋糕 :) - Dacav
我会定义一个函数,所有逻辑都在其中描述,所以(OLD."status" = 'Init' and NEW."status" != 'Queued')不会在触发器定义中,而是在函数中。对于被禁止的转换,返回null,这将防止更新(https://www.postgresql.org/docs/9.5/static/plpgsql-trigger.html)。 - Alexey Milogradov
显示剩余4条评论
1个回答

6

触发器是解决您问题的答案。

让我们考虑一个简单的表格:

CREATE TABLE world (id serial PRIMARY KEY, state VARCHAR);
insert into world (state) values ('big bang');
insert into world (state) values ('stars formation');
insert into world (state) values ('human era');

触发器调用的函数。在这里定义你的状态机逻辑。RAISE EXCEPTION(抛出异常)非常有用,因为你可以在此提供自定义消息。

CREATE FUNCTION check_world_change() RETURNS trigger as $check_world_change$
BEGIN
  IF OLD.state = 'big bang' AND NEW.state = 'human era' THEN
    RAISE EXCEPTION 'Dont skip stars';
  END IF;
  IF OLD.state = 'stars formation' AND NEW.state = 'big bang' THEN
    RAISE EXCEPTION 'Impossible to reverse order of things';
  END IF;
  RETURN NEW;
END;
$check_world_change$ LANGUAGE plpgsql;

并为您的表定义触发器:

CREATE TRIGGER check_world_change BEFORE UPDATE ON world 
  FOR EACH ROW EXECUTE PROCEDURE check_world_change();

现在,当您尝试更新其中一行的状态时,将会收到错误提示:
world=# select * from world;
 id |      state
----+-----------------
  2 | stars formation
  1 | human era
  3 | big bang
(3 rows)

world=# update world set state='human era' where state='big bang';
ERROR:  Wrong transition
world=# select * from world;
 id |      state
----+-----------------
  2 | stars formation
  1 | human era
  3 | big bang
(3 rows)

References:

https://www.postgresql.org/docs/9.5/static/plpgsql-trigger.html https://www.postgresql.org/docs/9.5/static/sql-createtrigger.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接