1

私たちのアプリケーション (内部の PostgreSQL (現在は 9.1、9.2 に移行中) を使用) では、リクエストの処理中に特定の行を INSERT (または UPDATE) および UPDATE します。したがって、次のようになります。

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

(処理は postgres と Java の間で分割され、postgres は可能なもの (他の列は NULL) を取得および挿入/更新し、コントロールを Java アプリに戻し、残りの列を更新します)。

悲しいことに、特定の行でのコミット後にデータの一貫性が失われる可能性があるアプリケーションの設計上の欠陥を発見しました (非常にまれな状況で、何年にもわたって 1 回だけ発生していました)。具体的には、columnA = 'someConstant' の場合、commit の後、columnB を NULL にすることはできません (ただし、最初の挿入/更新の後、2 回目の更新の前にすることはできます!)。

アプリケーションを再設計する際に、これが発生しないようにするための短期間の回避策を提供する必要があります。

私は現在、いわゆる CONSTRAINT TRIGGER で遊んでいます。COMMIT の直前に DEFERRED にすることができ、任意のチェックと RAISE EXCEPTION を実行できます (トランザクションが正常にロールバックされるため、これは適切です)。問題は、(INSERT,UPDATE) または (UPDATE,UPDATE) ステートメントがあることです。私が言ったように、前述の一貫性のない列は途中で有効ですが、トランザクションの最後に有効です。これが秘訣です: トリガーは 2 番目のステートメントに対してのみ起動する必要があります。FOR EACH ROW と FOR EACH STATEMENT が見つかりました。どちらも 2 回起動するため、これは役に立ちませんが、2 番目の 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 に答える