2013-02-14 71 views
1

在我們的應用程序(使用PostgreSQL引擎(當前爲9.1,移動到9.2)時,我們在處理某些請求時插入(或更新)和更新一個特定行 因此,喜歡:當應用程序觸發約束觸發器時進行微調

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

(遊行的Postgres和Java之間的劃分,Postgres的獲取和插入/更新所能,(其它列NULL),返回控制到Java應用程序,並更新剩餘的列)

不幸的是,我們在應用程序中發現了一個設計缺陷,在部分提交後可能導致數據不一致(特別罕見的情況下,多年來只有一次發生)。 更具體地說,如果columnA ='someConstant',columnB在COMMIT後不能爲NULL(但可以在第一次插入/更新之後,第二次更新之前!)。

在重新設計應用程序時,我們必須提供一些短期的解決方法來防止這種情況發生。

我目前在玩所謂的CONSTRAINT TRIGGER。它們可以在COMMIT之前被DEFERRED,並且它們可以執行任意檢查和RAISE EXCEPTION(這很好,因爲事務將被正常回滾​​)。 問題是我有(INSERT,UPDATE)或(UPDATE,UPDATE)語句。正如我所說,上述不一致的列在中間有效,但在交易結束時有效。所以這是一個竅門:觸發器應該只針對第二條語句觸發。我正在挖掘文檔,找到每行和每個語句。這並沒有幫助,因爲他們兩個都會觸發兩次,但我必須僅僅觸發第二個UPDATE語句。

任何想法?

回答

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,因爲它不適用於多個更新方案)。

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();