★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 16 | 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

39.3. Cによるトリガ関数の作成 #

本節ではトリガ関数とのインタフェースについて低レベルな詳細を説明します。 この情報はC言語でトリガ関数を作成する時にのみ必要です。 高レベルな言語で作成すれば、こうした詳細は代わりに扱ってもらえます。 たいていの場合、Cでトリガを作成する前に手続き言語を使用することを検討すべきです。 各手続き言語の文書で、その言語を使用したトリガの作成方法を説明します。

トリガ関数はversion 1関数マネージャインタフェースを使わなくてはいけません。

関数がトリガマネージャから呼び出される時は、通常の引数が渡されるのではなく、TriggerData構造体を指すcontextポインタが渡されます。 C関数は、トリガマネージャから呼び出されたのかどうかを以下のマクロを実行することで検査することができます。

CALLED_AS_TRIGGER(fcinfo)

これは以下に展開されます。

((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))

もしこれが真を返す場合、fcinfo->contextTriggerData *型にキャストし、指されたTriggerData構造体を使用することは安全です。 その関数は、TriggerData構造体やそれが指すどのようなデータも変更してはいけません

struct TriggerDatacommands/trigger.hの中で定義されています。

typedef struct TriggerData
{
    NodeTag          type;
    TriggerEvent     tg_event;
    Relation         tg_relation;
    HeapTuple        tg_trigtuple;
    HeapTuple        tg_newtuple;
    Trigger         *tg_trigger;
    TupleTableSlot  *tg_trigslot;
    TupleTableSlot  *tg_newslot;
    Tuplestorestate *tg_oldtable;
    Tuplestorestate *tg_newtable;
    const Bitmapset *tg_updatedcols;
} TriggerData;

メンバは下記のように定義されています。

type

常にT_TriggerDataです。

tg_event

その関数が呼び出されたイベントを記述します。 tg_eventを調べるためには下記のマクロを使うことができます。

TRIGGER_FIRED_BEFORE(tg_event)

トリガが操作の前に(before)発行された場合真を返します。

TRIGGER_FIRED_AFTER(tg_event)

トリガが操作の後に(after)発行された場合真を返します。

TRIGGER_FIRED_INSTEAD(tg_event)

トリガがINSTEAD OFで発行された場合真を返します。

TRIGGER_FIRED_FOR_ROW(tg_event)

トリガが行レベルのイベントで発行された場合真を返します。

TRIGGER_FIRED_FOR_STATEMENT(tg_event)

トリガが文レベルのイベントで発行された場合真を返します。

TRIGGER_FIRED_BY_INSERT(tg_event)

トリガがINSERTコマンドで発行された場合真を返します。

TRIGGER_FIRED_BY_UPDATE(tg_event)

トリガがUPDATEコマンドで発行された場合真を返します。

TRIGGER_FIRED_BY_DELETE(tg_event)

トリガがDELETEコマンドで発行された場合真を返します。

TRIGGER_FIRED_BY_TRUNCATE(tg_event)

トリガがTRUNCATEコマンドで発行された場合真を返します。

tg_relation

トリガの発行元のリレーションを記述する構造体へのポインタです。 この構造体についての詳細は、utils/rel.hを参照してください。 最も興味深いのは、tg_relation->rd_att(リレーションタプルの記述子)とtg_relation->rd_rel->relnameです(リレーション名、これはchar*ではなくNameDataです。 名前のコピーが必要な場合は、char*を得るためにSPI_getrelname(tg_relation)を使用してください)。

tg_trigtuple

トリガが発行された行へのポインタです。 これは挿入される、削除される、あるいは更新される行です。 もしINSERT/DELETEでこのトリガが発行された時、この行を別のもので置き換えたくない(INSERTの場合)場合や、その操作を飛ばしたくない場合は、これをこの関数から返してください。 外部テーブルのトリガに対しては、システム列の値はここでは指定されません。

tg_newtuple

トリガがUPDATEで発行された場合は、行の新しいバージョンへのポインタです。 INSERTもしくはDELETEの場合は、NULLです。 UPDATEイベントの時、この行を別のもので置き換えたくない場合や操作を飛ばしたくない場合は、これをこの関数から返してください。 外部テーブルのトリガに対しては、システム列の値はここでは指定されません。

tg_trigger

以下のようにutils/reltrigger.hで定義された、Trigger構造体へのポインタです。

typedef struct Trigger
{
    Oid         tgoid;
    char       *tgname;
    Oid         tgfoid;
    int16       tgtype;
    char        tgenabled;
    bool        tgisinternal;
    bool        tgisclone;
    Oid         tgconstrrelid;
    Oid         tgconstrindid;
    Oid         tgconstraint;
    bool        tgdeferrable;
    bool        tginitdeferred;
    int16       tgnargs;
    int16       tgnattr;
    int16      *tgattr;
    char      **tgargs;
    char       *tgqual;
    char       *tgoldtable;
    char       *tgnewtable;
} Trigger;

ここで、tgnameがトリガの名前、tgnargstgargs内の引数の数、tgargsCREATE TRIGGER文で指定された引数へのポインタの配列です。 他のメンバは内部でのみ使用されます。

tg_trigslot

tg_trigtupleを含むスロット、またはタプルが存在しない場合はNULLポインタです。

tg_newslot

tg_newtupleを含むスロット、またはタプルが存在しない場合はNULLポインタです。

tg_oldtable

tg_relationで指定するフォーマットの0以上の行を含むTuplestorestate型の構造体へのポインタです。 OLD TABLE遷移リレーションが存在しない場合はNULLポインタです。

tg_newtable

tg_relationで指定するフォーマットの0以上の行を含むTuplestorestate型の構造体へのポインタです。 NEW TABLE遷移リレーションが存在しない場合はNULLポインタです。

tg_updatedcols

UPDATEトリガに対しては、トリガコマンドにより更新された列を示すビットマップ集合です。 汎用のトリガ関数はこれを使って、変更されていない列を扱わないことで動作を最適化できます。

例として、属性番号attnum(1始まり)の列がこのビットマップ集合のメンバであるかどうか判定するために、bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))を呼び出します。

UPDATEトリガ以外のトリガに対しては、これはNULLになります。

SPIを使って遷移テーブルを参照するクエリを発行する方法については、SPI_register_trigger_dataを参照してください。

トリガ関数はHeapTupleポインタもしくはNULLポインタ(SQLのNULLではありません。 したがって、isNullは真にはなりません)のどちらかを返さなければなりません。 操作対象の行を変更したくない場合は、注意して、tg_trigtupletg_newtupleの適切な方を返してください。