当约束触发器应该触发时进行微调

时间:2013-02-14 15:03:33

标签: postgresql stored-procedures triggers

在我们的应用程序中(使用PostgreSQL(当前为9.1,移动为9.2),我们在处理某些请求时再次INSERT(或UPDATE)和UPDATE特定行。 所以它看起来像:

BEGIN
INSERT(pg), UPDATE(java)
XOR
UPDATE(pg), UPDATE(java)
COMMIT

(游戏分为postgres和java,postgres获取和插入/更新它可以(其他列为NULL),将控件返回给java应用程序,并更新剩余的列)。

遗憾的是,我们在应用程序中发现了一个设计缺陷,在特定行中提交后会导致数据不一致(在非常罕见的情况下,多年来只有一次出现)。 更具体地说,如果columnA ='someConstant',则在COMMIT之后columnB不能为空(但它可以在第一次插入/更新之后和第二次更新之前!)。

在重新设计应用程序时,我们必须提供一些短期解决方法来防止这种情况发生。

我正在玩所谓的CONSTRAINT TRIGGER。它们可以在COMMIT之前进行DEFERRED,它们可以进行任意检查和RAISE EXCEPTION(这很好,因为事务将正常回滚)。 问题是我有(INSERT,UPDATE)或(UPDATE,UPDATE)语句。正如我所说,提到的不一致列在中间有效,但在事务结束时有效。所以这就是诀窍:触发器只应触发第二个语句。我正在挖掘文档,找到每个行和每个声明。这没有用,因为它们都会触发两次,但我必须只触发第二个UPDATE语句。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

更新时的延迟约束触发器可以解决您的问题,但它不应检查NEW.column is NULL,因为NEW.column将是更新时的值,而不是提交的时间,即使触发器在提交时触发。

你可以做的是像这样的行级延迟约束触发器(假设table有一个名为pk的主键)来测试事务结束时的当前值:

DECLARE
 v column_type; -- to fetch the value at the end of the transaction
BEGIN
 SELECT column INTO v FROM table WHERE pk=NEW.pk;
 IF FOUND AND v IS NULL THEN
   RAISE ERROR 'Null not allowed in column';
 END IF
 RETURN new;
END;

如果表没有主键,则应找到一些不同的方法来获取最新的列值(而不是ctid,因为它在多个更新方案中不起作用。)

答案 1 :(得分:0)

谢谢丹尼尔。我正在测试这个:

CREATE OR REPLACE FUNCTION noFalseData() RETURNS trigger LANGUAGE plpgsql AS $$
DECLARE
 v text;
BEGIN
 SELECT column INTO v FROM active.table WHERE othercolumn = 'SOME_CONSTANT' AND pk=NEW.pk;
 IF FOUND AND v IS NULL THEN
   RAISE EXCEPTION 'Null not allowed in column';
 END IF;
 RETURN NULL;
END;
$$;

CREATE CONSTRAINT TRIGGER watchActCampUpd AFTER UPDATE
ON active.table DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
EXECUTE PROCEDURE noFalseData();