37.10. トリガプロシージャ

PL/pgSQLはトリガプロシージャの定義に使用できます。 トリガプロシージャは、CREATE FUNCTIONコマンドを使って、triggerという戻り値の型を持った引数のない関数として作成されます。 その関数は、たとえ、CREATE TRIGGERにて引数をとるものとしていたとしても、引数を持たないものと宣言しなければなりません。 トリガの引数は、後述する通り、TG_ARGV経由で渡されます。

PL/pgSQL関数がトリガとして呼び出された場合、いくつかの特殊な変数が自動的に最上位レベルのブロックで作成されます。 それらを以下に示します。

NEW

RECORDデータ型。 この変数は行レベルのトリガでのINSERT/UPDATE操作によって更新された、新しいデータベースの行を保持します。 この変数は文レベルのトリガではNULLです。

OLD

RECORDデータ型。 この変数は、行レベルのトリガでのUPDATE/DELETE操作によって更新される前のデータベースの行を保持します。 この変数は文レベルのトリガではNULLです。

TG_NAME

nameデータ型。 実際に発行されたトリガの名前を持つ変数。

TG_WHEN

textデータ型。 トリガの定義に依存したBEFOREまたはAFTERという文字列。

TG_LEVEL

textデータ型。 トリガの定義に依存したROWまたはSTATEMENTという文字列。

TG_OP

textデータ型。 トリガを発行した操作を示す、INSERT, UPDATEまたはDELETEという文字列。

TG_RELID

oidデータ型。 このトリガの呼び出し元になるテーブルのオブジェクトID。

TG_RELNAME

nameデータ型。 このトリガの呼び出し元になるテーブルの名前。

TG_NARGS

integer型。 CREATE TRIGGER文におけるトリガプロシージャに与えられる引数の数。

TG_ARGV[]

text型の配列型。 CREATE TRIGGER文での引数。 このインデックスは0から始まります。 無効なインデックス(0未満やtg_nargs以上)は、NULL値という結果になります。

トリガ関数はNULLまたは、トリガの発行元になったテーブルの構造を正確に持ったレコード/行を返さなければなりません。

BEFOREとして発行された行レベルトリガがNULLを返す場合には、トリガマネージャに実際の行への操作を取りやめるように通知します(つまり、その後にトリガが発行されず、そのINSERT/UPDATE/DELETEはその行に対して実行されません)。 非NULL値を返す場合には、その操作はその行値で処理されます。 元のNEWの値と異なる行値を返すことは、挿入、更新される値を変更することに注意して下さい。 (しかし、DELETEの場合は直接影響しません。) 格納すべき行を変更するために、NEWの個々の値を直接置き換え、そのNEWを返すことも、新しいレコード/行を完全に作成して返すことも可能です。

文レベルのBEFOREまたはAFTERトリガ、行レベルのAFTERトリガの戻り値は常に無視されます。 NULLとしても構いません。 しかし、これらの種類のトリガでも、エラーを発生させることで操作全体を中断させることが可能です。

例37-1PL/pgSQLのトリガ関数の例を示します。

例 37-1. PL/pgSQLトリガプロシージャ

このトリガの例では、テーブルの行が挿入または更新された時には必ず、現在のユーザ名と時刻がその行に入っていることを確実にします。 そして、従業員名が与えられていることとその給料が正の値であることを確認します。

CREATE TABLE emp (
    empname text,
    salary integer,
    last_date timestamp,
    last_user text
);

CREATE FUNCTION emp_stamp() RETURNS trigger AS '
    BEGIN
        -- empname とsalary が与えられていることをチェック
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION ''empname cannot be null'';
        END IF;
        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION ''% cannot have null salary'', NEW.empname;
        END IF;

        -- 支払い時に問題が起こらないように
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION ''% cannot have a negative salary'', NEW.empname;
        END IF;

        -- 誰がいつ変更したかを記録
        NEW.last_date := ''now'';
        NEW.last_user := current_user;
        RETURN NEW;
    END;
' LANGUAGE plpgsql;

CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
    FOR EACH ROW EXECUTE PROCEDURE emp_stamp();