1

在我们的应用程序中(使用 PostgreSQL 引擎盖(目前是 9.1,移动到 9.2)我们在处理某些请求时再次插入(或更新)和更新特定行。所以它看起来像:

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

(进程在 postgres 和 java 之间划分,postgres 获取并插入/更新它可以的内容(其他列 NULL),将控制权返回给 java 应用程序,并更新剩余的列)。

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

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

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

有任何想法吗?

4

2 回答 2

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因为它在多次更新场景中不起作用)。

于 2013-02-14T19:40:34.677 回答
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();
于 2013-02-15T09:05:34.550 回答