完全な例

PostgreSQL 8.4.1文書
前のページ | 巻戻し | 第 35章トリガ | 早送り | 次のページ

# 35.4. 完全な例

C言語で作成したトリガ関数に関するとても簡単な例をここに示します （手続き言語で作成したトリガの例は、その手続き言語の文書に記載されています）。

`trigf`関数は、`ttest`テーブル内にある行数を報告し、問い合わせが`x`にNULL値を挿入しようとしていた場合は、その操作を飛ばします （つまり、このトリガは、トランザクションを中断させないNOT NULL制約のような動作をします）。

まず、以下のようにテーブルを定義します。

    CREATE TABLE ttest (
        x integer
    );

以下がトリガ関数のソースコードです。

    #include "postgres.h"

    #include "executor/spi.h"       /* これはSPIを使用する場合に必要なもの */
    #include "commands/trigger.h"   /* これはトリガで必要なもの */

    extern Datum trigf(PG_FUNCTION_ARGS);

    PG_FUNCTION_INFO_V1(trigf);

    Datum
    trigf(PG_FUNCTION_ARGS)
    {
        TriggerData *trigdata = (TriggerData *) fcinfo->context;
        TupleDesc   tupdesc;
        HeapTuple   rettuple;
        char       *when;
        bool        checknull = false;
        bool        isnull;
        int         ret, i;

        /* トリガとして呼び出されたかどうかを確認 */
        if (!CALLED_AS_TRIGGER(fcinfo))
            elog(ERROR, "trigf: not called by trigger manager");

        /* エクゼキュータに返すタプル */
        if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
            rettuple = trigdata->tg_newtuple;
        else
            rettuple = trigdata->tg_trigtuple;

        /* NULL値をチェック */
        if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
            && TRIGGER_FIRED_BEFORE(trigdata->tg_event))
            checknull = true;

        if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
            when = "before";
        else
            when = "after ";

        tupdesc = trigdata->tg_relation->rd_att;

        /* SPIマネージャに接続 */
        if ((ret = SPI_connect()) < 0)
            elog(ERROR, "trigf (fired %s): SPI_connect returned %d", when, ret);

        /* テーブル中の行数を取得 */
        ret = SPI_exec("SELECT count(*) FROM ttest", 0);

        if (ret < 0)
            elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret);

        /* count(*)はint8を返す。変換に注意してください*/
        i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
                                        SPI_tuptable->tupdesc,
                                        1,
                                        &isnull));

        elog (INFO, "trigf (fired %s): there are %d rows in ttest", when, i);

        SPI_finish();

        if (checknull)
        {
            SPI_getbinval(rettuple, tupdesc, 1, &isnull);
            if (isnull)
                rettuple = NULL;
        }

        return PointerGetDatum(rettuple);
    }

ソースコードをコンパイルした後に(コンパイルについては項34.9.6を見てください)、以下のように関数とトリガを宣言します。

    CREATE FUNCTION trigf() RETURNS trigger
        AS 'filename'
        LANGUAGE C;

    CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
        FOR EACH ROW EXECUTE PROCEDURE trigf();

    CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
        FOR EACH ROW EXECUTE PROCEDURE trigf();

これで、トリガの操作を確認することができます。

    => INSERT INTO ttest VALUES (NULL);
    INFO:  trigf (fired before): there are 0 rows in ttest
    INSERT 0 0

    -- 挿入操作は飛ばされ、また、AFTERトリガも発行されません。

    => SELECT * FROM ttest;
     x
    ---
    (0 rows)

    => INSERT INTO ttest VALUES (1);
    INFO:  trigf (fired before): there are 0 rows in ttest
    INFO:  trigf (fired after ): there are 1 rows in ttest
                                           ^^^^^^^^
                                 可視性の説明を思い出してください。
    INSERT 167793 1
    vac=> SELECT * FROM ttest;
     x
    ---
     1
    (1 row)

    => INSERT INTO ttest SELECT x * 2 FROM ttest;
    INFO:  trigf (fired before): there are 1 rows in ttest
    INFO:  trigf (fired after ): there are 2 rows in ttest
                                           ^^^^^^
                                 可視性の説明を思い出してください。
    INSERT 167794 1
    => SELECT * FROM ttest;
     x
    ---
     1
     2
    (2 rows)

    => UPDATE ttest SET x = NULL WHERE x = 2;
    INFO:  trigf (fired before): there are 2 rows in ttest
    UPDATE 0
    => UPDATE ttest SET x = 4 WHERE x = 2;
    INFO:  trigf (fired before): there are 2 rows in ttest
    INFO:  trigf (fired after ): there are 2 rows in ttest
    UPDATE 1
    vac=> SELECT * FROM ttest;
     x
    ---
     1
     4
    (2 rows)

    => DELETE FROM ttest;
    INFO:  trigf (fired before): there are 2 rows in ttest
    INFO:  trigf (fired before): there are 1 rows in ttest
    INFO:  trigf (fired after ): there are 0 rows in ttest
    INFO:  trigf (fired after ): there are 0 rows in ttest
                                           ^^^^^^
                                 可視性の説明を思い出してください。
    DELETE 2
    => SELECT * FROM ttest;
     x
    ---
    (0 rows)

`src/test/regress/regress.c`と`contrib/spi`にはもっと複雑な例があります。

前のページ | ホーム | 次のページ
Cによるトリガ関数の作成 | 上に戻る | ルールシステム