34.1. トリガ動作の概要

トリガとは、データベースが、ある特定の操作が行われた時に常に自動的に実行しなければならない特定の機能に関する規定です。 トリガを、INSERTUPDATEまたはDELETE操作の前後に、行を変更する度、あるいはSQL行ごとに実行するように定義することができます。 トリガイベントが起こると、トリガ関数がそのイベントを扱う適切な時点で呼び出されます。

トリガ関数は、トリガ自体が作成される前までに定義しておく必要があります。 トリガ関数は、引数を取らない、trigger型を返す関数として宣言される必要があります (トリガ関数は、通常の関数で使用される引数という形ではなく、TriggerData構造体で入力を受け取ります)。

適切なトリガ関数が作成されると、CREATE TRIGGERを使用してトリガを構築することができます。 同一のトリガ関数を複数のトリガに使用することができます。

PostgreSQLは、行単位のトリガと文単位のトリガの両方を提供します。 行単位のトリガでは、トリガを発行した文によって影響を受ける行ごとにトリガ関数が呼び出されます。 反対に、文単位のトリガでは、適切な文が実行された時に、その文で何行が影響を受けたかどうかは関係なく、一度だけ呼び出されます。 特に、行に影響を与えない文であっても、適切な文単位のトリガがあれば実行されます。 この2種類のトリガはそれぞれ行レベルトリガと文レベルトリガ<と呼ばれることがあります。

また、トリガはbeforeトリガとafterトリガとに分けられます。 文レベルのbeforeトリガは、文が何かを始める前に自然に発行され、文レベルのafterトリガは文の本当に最後に発行されます。 行レベルのbeforeトリガは、特定の行が操作される直前に発行され、行レベルのafterトリガは文の終わり(ただし、全ての文レベルのafterトリガの前)に発行されます。

文単位のトリガによって呼び出されるトリガ関数は常にNULLを返さなければなりません。 行単位のトリガによって呼び出されるトリガ関数は呼び出し元のエクゼキュータにテーブル行(HeapTuple型の値)を返すように選択することができます。 操作前に発行された行レベルのトリガでは以下の選択肢があります。

これらの動作をさせたくない行レベルのBEFOREトリガについては、渡された行(つまり、INSERTおよびUPDATEトリガではNEW行、DELETEの場合はOLD行)と同じ行結果を返すように気を付ける必要があります。

操作の後に発生する行レベルトリガでは戻り値は無視されますので、これらはよくNULLを返します。

同一リレーション、同一イベントに対して1つ以上のトリガが定義された場合、トリガはその名前のアルファベット順に発生します。 BEFOREトリガの場合では、各トリガで返される、変更された可能性がある行が次のトリガの入力となります。 もし、あるBEFOREトリガがNULLを返したら、操作はその行で中断し、残りのトリガは発生しません。

通常、行レベルのBEFOREトリガは、挿入あるいは更新される予定のデータの検査や変更のために使用されます。 例えば、BEFOREトリガは、timestamp型の列に現在時刻を挿入するために、あるいは行の2つの要素の整合性を検査するために使用される可能性があります。 行レベルのAFTERトリガは、ほとんど常識的に他のテーブルに更新を伝播させるために、あるいは他のテーブルとの整合性を検査するために使用されます。 こうした仕事の切り分け理由は、AFTERトリガは行の最終値を見ることができ、BEFOREトリガは見ることができないという点です。 トリガをBEFOREにするかAFTERにするかを決める時に特別な理由がないのであれば、操作の情報を行が終わるまで保持する必要がない分、BEFOREを使う方が効率的です。

トリガ関数がSQLコマンドを処理する場合、これらの問い合わせがトリガを再度発行することがあります。 これはカスケードされたトリガと呼ばれます。 カスケードの段数に直接的な制限はありません。 カスケードの場合、同じトリガを再帰的に呼び出すことが可能です。 例えば、INSERTトリガで同じテーブルに追加の行を挿入する問い合わせが実行された場合、その結果としてINSERTトリガが再度発行されます。 こうした状況で無限再帰を防ぐのは、トリガプログラマの責任です。

トリガを定義する時、そのトリガ用の引数を指定することができます。 トリガ定義に引数を含めた目的は、似たような要求の異なるトリガに同じ関数を呼び出すことができるようにすることです。 例えば、2つの列名を引数とし、片方に現在のユーザをもう片方に現在のタイムスタンプを取る、汎化トリガ関数があるとします。 適切に作成すれば、この関数が特定のトリガの発行元となるテーブルに依存することはなくなります。 同じ関数を使用して、例えば、トランザクションテーブルに作成記録を自動的に登録させるために、適切な列を持つ任意のテーブルのINSERTイベントに使用することができます。 また、UPDATEとして定義すれば、最終更新イベントを追跡するために使用することも可能です。

トリガをサポートするプログラミング言語はそれぞれ独自の方法で、トリガ関数で利用できるトリガの入力データを作成します。 この入力データにはトリガイベント種類(例えばINSERTUPDATEなど、CREATE TRIGGERで指定された全ての引数)が含まれます。 行レベルトリガの入力データには、INSERTおよびUPDATEトリガの場合はNEW行が、UPDATEおよびDELETEトリガの場合はOLD行が含まれます。 文レベルトリガには現在、文によって変更される個々の行を検査するための手段がありません。