他のバージョンの文書 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

38.1. トリガ動作の概要

トリガとは、データベースが、ある特定の操作が行われた時に常に自動的に実行しなければならない特定の機能に関する規定です。 トリガはテーブル(パーティション化されているかどうかにかかわらず)、ビュー、外部テーブルに付与することができます。

テーブルおよび外部テーブル上では、トリガをINSERTUPDATEまたはDELETE操作の前後に、行を変更する度、あるいはSQL文ごとに実行するように定義することができます。 さらに、UPDATEトリガについては、特定のカラムがUPDATE文のSET句の対象になった時のみ発動するよう設定することができます。 また、トリガはTRUNCATE文についても発動できます。 トリガイベントが起こると、トリガ関数がそのイベントを扱う適切な時点で呼び出されます。

ビュー上では、トリガをINSERTUPDATEまたはDELETE操作の代わりに実行するものとして定義できます。 そうしたINSTEAD OFトリガは、ビュー内の変更を行うために必要となる行それぞれに対して一度発行されます。 ビューの元になっている基底テーブルへの必要な変更の実施、そして必要に応じて、ビュー上で見えるであろう変更された行を返却するのは、トリガ関数の責任です。 ビューへのトリガは、SQL文ごとに、INSERTUPDATEまたはDELETE操作の前後で実行させるよう定義することもできます。 しかし、そうしたトリガは、ビューにINSTEAD OFトリガがあるときにだけ発行されます。 INSTEAD OFトリガを定義しない場合は、ビューを操作しようとする文は、元になる基底テーブルに影響を与える文に書き換えなければなりません。 その結果、発行されるトリガは、基底テーブルに付けられたトリガとなります。

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

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

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

また、トリガはそれらが操作のまたは代わりのどれで実行されるかに応じて分けられます。 これらはそれぞれBEFOREトリガ、AFTERトリガ、そしてINSTEAD OFトリガと呼ばれます。 文レベルのBEFOREトリガは、もちろん文が何かを始める前に発行され、文レベルのAFTERトリガは文の本当に最後に発行されます。 これらのタイプのトリガはテーブル、ビュー、あるいは外部テーブルに定義できます。 行レベルのBEFOREトリガは、特定の行が操作される直前に発行され、行レベルのAFTERトリガは文の終わり(ただし、全ての文レベルのAFTERトリガの前)に発行されます。 これらのタイプのトリガは、外部テーブルに定義できますが、ビューには定義できません。 INSTEAD OFトリガはビューにのみ定義され、行レベルのみが許されます。 つまり、ビュー上のそれぞれの行で処理が必要と判断された場合には、即座に発動します。

継承あるいはパーティション階層において、親テーブルをターゲットとする文は、影響を受けた子テーブルの文レベルトリガを発動しません。 すなわち、親テーブルの文レベルトリガのみが発動します。 しかし、影響を受けた子テーブルの行レベルトリガは発動します。

INSERTON CONFLICT DO UPDATE句を含む場合、EXCLUDED列が参照されていると、行単位BEFORE INSERTトリガおよび行単位BEFORE UPDATEトリガの両方の効果が適用され、それが更新後の行の最後の状態から明らかな場合がありえます。 ただし、両方の行レベルのBEFOREトリガを実行するためにEXCLUDEDの参照が必要なわけではありません。 驚くような結果の可能性について、BEFORE INSERTBEFORE UPDATEの両方の文単位トリガーがあり、それらがいずれも挿入あるいは更新対象の行に影響を与える場合に考慮すべきです(これは更新が冪等ではないが、ほぼ同等であるときには、それでも問題になります)。 文単位のUPDATEトリガはON CONFLICT DO UPDATEが指定されたとき、そのUPDATEによって行が影響を受けたかどうかに関わらず(そしてその代替であるUPDATE部分が実行されたかどうかに関わらず)実行されることに注意してください。 ON CONFLICT DO UPDATE句のあるINSERTでは、まず文単位のBEFORE INSERTトリガ、次に文単位のBEFORE UPDATEトリガ、次いで文単位のAFTER UPDATEトリガ、最後に文単位のAFTER INSERTトリガを実行します。

あるパーティション化されたテーブルに適用されたUPDATEの結果、行が他のパーティションに移動することになるなら、元のパーティションでDELETEし、続いて新しいパーティションにINSERTする操作として実行されます。 この場合、すべての行レベルBEFORE UPDATEトリガとBEFORE DELETEトリガが元のパーティションで発動します。 そして、すべての行レベルBEFORE INSERTトリガが移動先のパーティションで発動します。 これらのトリガが移動対象の行に対して影響を及ぼす際に、驚くべき結果となる可能性を考慮しておくべきでしょう。 AFTER ROWトリガに関しては、AFTER DELETEAFTER INSERTトリガが適用されます。しかし、AFTER UPDATEトリガは適用されません。なぜなら、UPDATEDELETEINSERTに変換されるからです。 文レベルのトリガに関しては、たとえ行の移動が起こったとしてもDELETEトリガもINSERTトリガも発動されません。UPDATE文中に現れた対象テーブルに定義されたUPDATEトリガだけが発動されます。

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

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

行レベルのINSTEAD OFトリガは、ビューの元となった元テーブルのデータをまったく変更しないことを表すNULL、または、渡されたビューの行(INSERTUPDATE操作の場合NEW行、DELETE操作の場合OLD行)を返さなければなりません。 非NULLの戻り値は、そのトリガがビューにおいて必要なデータ変更を実行したことを通知するために使用されます。 これにより影響を受けた行数を数えるカウンタは増加されます。 INSERTUPDATE操作のみ、トリガは戻す前にNEW行を変更することができます。 これはINSERT RETURNINGまたはUPDATE RETURNINGで返されるデータを変更しますので、ビューが提供されたデータと正確に同じ結果を返さない場合に有益です。

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

生成列に対してはいくつか考慮が必要です。 格納された生成列は、BEFOREトリガの後、AFTERトリガの前に計算されます。 そのため、生成される値はAFTERトリガで調べることができます。 BEFOREトリガでは、皆さんが期待している通りOLD行は以前の生成された値を含んでいますが、NEW行は新しく生成される値をまだ含んでおらず、アクセスすべきではありません。 C言語インタフェースでは、この時点では列の内容は未定義です。高レベルプログラム言語は、BEFOREトリガ内ではNEW行の生成列へのアクセスを避けるべきです。 BEFOREトリガでの生成列の値の変更は無視され、上書きされます。

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

トリガ定義は、トリガを発動するかどうかをWHEN句の論理条件で指定することも可能です。行レベルトリガにおいて、WHEN条件は行の列の古い値と(あるいは)新しい値を検索することができます。(あまり有用ではありませんが、文レベルトリガでもWHEN条件で同じことができます。)BEFOREトリガでは、実質的にトリガ関数の開始時と同じ条件で検査できるように、WHEN条件の評価が関数の実施直前になされます。しかしAFTERトリガでは、WHEN条件の評価は行の更新直後に行われ、文の終わり(コミット時)にトリガを発動するためのイベントを待ち行列に入れるかどうかを決めます。そのため、あるAFTERトリガのWHEN条件が真を返さなかった場合は、イベントを待ち行列に入れる必要も文の終わりに行を再取得する必要もありません。これは、大量の行の変更が発生するけれども、トリガがその内の少数の行に対してのみ発動させる必要がある、といった文の処理速度を大幅に上げる効果があります。INSTEAD OFトリガはWHEN条件をサポートしていません。

通常、行レベルの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行が含まれます。

デフォルトでは、文レベルトリガには文によって変更された個々の行を検査するための手段がありません。 しかし、トリガがアクセスできる影響を受けた行の集合を作成するために、AFTER STATEMENTトリガは、遷移テーブル(transition tables)の作成を依頼することができます。 AFTER ROWトリガも遷移テーブルを依頼できるので、発動中の個々の行における変更だけでなく、テーブル全体におけるすべての変更を見ることができます。 遷移テーブルを検査する方法も使用中のプログラミング言語に依存しますが、典型的な方法は、トリガ関数の中で発行するSQLコマンドでアクセスできる、読み込み専用の一時テーブルのように振る舞う遷移テーブルを作成することです。