在我们的应用程序中(使用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语句。
有什么想法吗?
答案 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();