設定する列の名前を(BEFORE UPDATE
)トリガー、OLD
値に設定し、入ってくるものはすべて無視します。以下を試しました。
CREATE OR REPLACE FUNCTION prevent_column_update() RETURNS TRIGGER AS $$
DECLARE
col TEXT := TG_ARGV[0];
BEGIN
EXECUTE format('SELECT ($1).%I INTO ($2).%I', col, col) USING OLD, NEW;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
そして、次のように使用します:
CREATE TRIGGER request_protect_date_price_value
BEFORE UPDATE OF date_price ON requests
FOR EACH ROW EXECUTE PROCEDURE prevent_column_update('date_price');
しかし、更新すると失敗します:
ERROR: syntax error at or near "("
LINE 1: SELECT ($1).date_price INTO ($2).date_price
^
QUERY: SELECT ($1).date_price INTO ($2).date_price
エラーは、INTO
句がnot SQLコマンドの一部であることです。これはplpgsqlコマンドEXECUTE
の一部です。
そして、動的フィールド名は、SQLでもPL/pgSQLでも、現在は不可能です。ただし、この制限を回避する方法はいくつかあります。
組み込みの JSON関数json_populate_record()
を使用して同様のトリックを行うことができますが、これは現在文書化されておらず、Postgresの将来のバージョンでは削除される可能性があります。
確実な方法は、文書化された#=
operator追加の hstore モジュールの。データベースごとに1回モジュールをインストールする
CREATE EXTENSION IF NOT EXISTS hstore;
次に:
CREATE OR REPLACE FUNCTION prevent_column_update()
RETURNS TRIGGER AS
$func$
DECLARE
_col text := quote_ident(TG_ARGV[0]);
_old_val text;
_new_val text;
BEGIN
EXECUTE format('SELECT $1.%1$I, $2.%1$I', _col)
INTO _old_val, _new_val -- part of plpgsql command
USING OLD, NEW;
IF _old_val IS DISTINCT FROM _new_val THEN -- only if it actually changed
NEW := NEW #= hstore(_col, _old_val); -- hstore operator #=
END IF;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
hstore
はテキスト文字列で動作することに注意してください。他のデータタイプの値はtext
にキャストされて戻されます。これは、考えられるすべてのデータタイプで機能します。ただし、一部のタイプでは問題が発生する可能性があります(浮動小数点数の丸めエラーなど)。
そして、このトリガー定義は、ケースを完全にするためのものです。
CREATE TRIGGER tbl_upbef_nope
BEFORE UPDATE ON tbl -- your table here
FOR EACH ROW
EXECUTE PROCEDURE prevent_column_update('date_price');
この例では、列名はcasesensitiveです。これは、列名が識別子ではなく文字列として渡されるためです。
各テーブルに動的SQLを含まない新しいプレーントリガー関数を作成するだけです。手間が減り、パフォーマンスが向上します。コードの重複に関する箇条書きをバイトします。
CREATE OR REPLACE FUNCTION prevent_column_update()
RETURNS TRIGGER AS
$func$
BEGIN
NEW.date_price:= OLD.date_price; -- unconditionally
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
引き金:
CREATE TRIGGER tbl_upbef_nope
BEFORE UPDATE OF date_price ON tbl -- your column and table here
FOR EACH ROW EXECUTE PROCEDURE prevent_column_update(); -- no param
チェックをトリガー自体に移動したので、列が更新されない限り、関数は実行されません。これは、同じテーブルの追加のトリガーによって回避される可能性があることに注意してください( マニュアルの引用 )。
トリガーは、リストされた列の少なくとも1つが
UPDATE
コマンドのターゲットとして言及されている場合にのみ起動します。
そのため、そのような追加のトリガーを除外できない場合は、UPDATE
で無条件にトリガーを起動し、代わりにトリガー関数の変更を確認してください。