 <chapter id="triggers">
  <!-- <title>Triggers</title>-->
  <title>トリガ</title>
<indexterm><primary sortas="とりが">トリガ</primary></indexterm><indexterm><primary>PostgreSQL</primary><secondary sortas="とりが">トリガ</secondary></indexterm>
  <para>
   <!-- 
   <productname>Postgres</productname> has various server-side function
   interfaces. Server-side functions can be written in SQL, PLPGSQL,
   TCL, or C. Trigger functions can be written in any of these
   languages except SQL. Note that STATEMENT-level trigger events are not
   supported in the current version. You can currently specify BEFORE or
   AFTER on INSERT, DELETE or UPDATE of a tuple as a trigger event.
   -->
   <productname>Postgres</productname>はさまざまなサーバーサイド関数のインターフェイスを持っています。サーバーサイド関数は、SQL、PLPGSQL、TCL、Cで書くことができます。トリガ関数は、SQLを除くこれらのどの言語でも使って書くことができます。現バージョンでは、STATEMENTレベルのトリガイベントはサポートされていませんので注意してください。現在はタプルのINSERT、DELETE、UPDATEの前（BEFORE）または後（AFTER）をトリガイベントとして指定できます。

  </para>

  <sect1 id="trigger-create">
   <!-- <title>Trigger Creation</title> -->
   <title>トリガの生成</title>

   <para>
    <!-- 
    If a trigger event occurs, the trigger manager (called by the Executor)
    sets up a TriggerData information structure (described below) and calls
    the trigger function to handle the event.
    -->

    トリガイベントが発生すると、（エクゼキュータによって呼び出される）トリガマネージャはTriggerData情報構造体（後述）を設定し、そのイベントを扱うトリガ関数を呼び出します。<indexterm><primary sortas="とりが">トリガ</primary><secondary sortas="とりがまねーじゃ">トリガマネージャ</secondary></indexterm>

   </para>

   <para>
    <!-- 
    The trigger function must be created before the trigger is created as a
    function taking no arguments and returning opaque.  If the function is
    written in C, it must use the "version 1" function manager interface.
    -->

    トリガが生成される前に、トリガ関数は、引数をとらない、opaque型を返す関数として作成されていなければいけません。もし関数がCで書かれている場合、「Version-1」関数マネージャインターフェイスを使わなくてはいけません。

   </para>

   <para>
    <!-- The syntax for creating triggers is as follows: -->
    トリガを生成するための構文は次のとおりです。<indexterm><primary>CREATE TRIGGER</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>CREATE TRIGGER</secondary></indexterm>
</para>

<programlisting>
CREATE TRIGGER <replaceable>trigger</replaceable> [ BEFORE | AFTER ] [ INSERT | DELETE | UPDATE [ OR ... ] ]
    ON <replaceable>relation</replaceable> FOR EACH [ ROW | STATEMENT ]
    EXECUTE PROCEDURE <replaceable>procedure</replaceable>
     (<replaceable>args</replaceable>);
</programlisting>

<para>
    <!-- where the arguments are: -->
    引数は以下のとおりです。
 </para>

    <variablelist>
     <varlistentry>
      <term>
       <replaceable>trigger</replaceable>
      </term>
      <listitem>
       <para>
	<!-- The name of the trigger is
	used if you ever have to delete the trigger.
	It is used as an argument to the <command>DROP TRIGGER</command> 
        command. -->

        トリガ名は、トリガを削除する必要になったときにも使われます。<command>DROP TRIGGER</command>コマンドの引数として使われます。

        
       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>BEFORE</term>
      <term>AFTER</term>
      <listitem>
       <para>
	<!-- Determines whether the function is called before or after
	the event. -->

        関数がイベントが発生する前に呼ばれるのか後に呼ばれるのかを決定します。


       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>INSERT</term>
      <term>DELETE</term>
      <term>UPDATE</term>
      <listitem>
       <para>
        <!-- 
	The next element of the command determines on what event(s) will 
        trigger	the function.  Multiple events can be specified separated
        by OR. -->

        次の要素が、どのコマンドのイベント（複数可）で関数を呼び出すかを決定します。複数のイベントをORで区切って指定できます。


       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term><replaceable>relation</replaceable></term>
      <listitem>
       <para>
	<!-- The relation name determines which table the event applies to.
        -->
        リレーション名はどのテーブルにイベントを適用するかを決定します。

       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>ROW</term>
      <term>STATEMENT</term>
      <listitem>
       <para>
        <!-- 
	The FOR EACH clause determines whether the trigger is fired for each
	affected row or before (or after) the entire statement has completed.
        -->

        FOR EACH文はトリガを行が影響を受けるたびに発行するか、文全体が終了する前（または後）に発行するかを決定します。


       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term><replaceable>procedure</replaceable></term>
      <listitem>
       <para>
	<!-- The procedure name is the function called. -->
        プロシージャ名は呼び出される関数を示します。

       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term><replaceable>args</replaceable></term>
      <listitem>
       <para>
        <!-- 
	The arguments passed to the function in the TriggerData structure.
	The purpose of passing arguments to the function is to allow different
	triggers with similar requirements to call the same function.
        -->

        関数の引数は、関数にTriggerData構造体の中に格納されて渡されます。引数を関数に渡す目的は、同一関数の呼び出しによって要求仕様が似ている異なるトリガを扱えるようにすることです。


       </para>

       <para>
	<!-- Also, <replaceable>procedure</replaceable>
	may be used for triggering different relations (these
	functions are named as "general trigger functions").
        -->

        また、<replaceable>procedure</replaceable>は異なるリレーションのトリガに使うこともできます（こういった関数は「汎用トリガ関数」と呼ばれます）。<indexterm><primary sortas="とりが">トリガ</primary><secondary sortas="はんようとりがかんすう">汎用トリガ関数</secondary></indexterm><indexterm><primary sortas="はんようとりがかんすう">汎用トリガ関数</primary></indexterm>


       </para>

       <para>
	<!-- As example of using both features above, there could be a general
	function that takes as its arguments two field names and puts the 
        current
	user in one and the current timestamp in the other. This allows 
        triggers to be written on INSERT events to automatically track 
        creation of records in a transaction table for example. 
        It could also be used as a "last updated"
	function if used in an UPDATE event. -->

        上記の両機能を使う例として、片方に現在のユーザーを、もう片方に現時刻を格納した2つのフィールドの名前を引数としてとる汎用関数があります。たとえば、この関数をINSERTイベント時に使用すれば、トランザクションテーブル内のレコードの作成を検出して、自動的に書き込みを行わせることができます。または、UPDATEイベント時に使用すれば、「最終更新」関数として使用することができます。


       </para>
      </listitem>
     </varlistentry>
    </variablelist>

   <para>
    <!-- Trigger functions return HeapTuple to the calling Executor.  This
    is ignored for triggers fired after an INSERT, DELETE or UPDATE operation
    but it allows BEFORE triggers to: -->

    トリガ関数は呼び出したエクゼキュータにHeapTupleを返します。これはINSERT、DELETE、UPDATEの後に発行されるトリガでは無視されますが、BEFOREのトリガでは、以下を行うことができます。
</para>

    <itemizedlist>
     <listitem>
      <para>
       <!-- 
       Return NULL to skip the operation for the current tuple (and so the
       tuple will not be inserted/updated/deleted). -->

       NULLを返して、現タプルへの操作をとばします（そして、このタプルの挿入、更新、削除は行われなくなります）。

      </para>
     </listitem>

     <listitem>
      <para>
       <!-- 
       Return a pointer to another tuple (INSERT and UPDATE only) which will
       be inserted (as the new version of the updated tuple if UPDATE) instead
       of original tuple. -->

       本来のタプルではなく他のタプルのポインタを返して、それを（UPDATEの場合は更新されたタプルの新しいバージョンとして）挿入させます（INSERTとUPDATEのみ）。

      </para>
     </listitem>
    </itemizedlist>

   <para>
    <!-- 
    Note that there is no initialization performed by the CREATE TRIGGER
    handler.  This will be changed in the future.  Also, if more than one 
    trigger is defined for the same event on the same relation, 
    the order of trigger firing is unpredictable. 
    This may be changed in the future. -->

    CREATE TRIGGERハンドラによって初期化は行われないことに注意してください。このことは今後変更する予定です。また、同一リレーションの同一イベントに対して複数トリガが定義された場合は、トリガの発行順は予測できません。これも今後変更されるかもしれません。<indexterm><primary sortas="とりが">トリガ</primary><secondary sortas="しょきか">初期化</secondary></indexterm>

   </para>

   <para>
    <!-- 
    If a trigger function executes SQL-queries (using SPI) then these queries
    may fire triggers again. This is known as cascading triggers.  There is no
    explicit limitation on the number of cascade levels. -->

    トリガ関数が（SPIを使用して）SQL問い合わせを処理する場合、これらの問い合わせがトリガを再度発行することがあります。これはカスケードされたトリガと呼ばれます。カスケードの段数に明確な制限はありません。


   </para>

   <para>
    <!-- 
    If a trigger is fired by INSERT and inserts a new tuple in the same
    relation then this trigger will be fired again.  Currently, there is 
    nothing provided for synchronization (etc) of these cases but this
    may change.  At the moment, there is function funny_dup17() in the 
    regress tests which uses some techniques to stop recursion (cascading) 
    on itself... -->

    INSERTによって発行されたトリガが同じリレーションに新しいタプルを挿入する場合、このトリガが再度発行されます。現状ではこの場合の同期（などの）機能は提供されていませんが、これも変更される可能性があります。現段階では、リグレッションテストの中に、自身への再帰（カスケーディング）を停止させるための数個のテクニックを使用したfunny_dup17()という関数があります。

   </para>
  </sect1>

  <sect1 id="trigger-manager">
   <!-- <title>Interaction with the Trigger Manager</title> -->
   <title>トリガマネージャとの関係</title>
<indexterm><primary sortas="とりが">トリガ</primary><secondary sortas="とりがまねーじゃ">トリガマネージャ</secondary></indexterm>
   <para>
    <!-- 
    This section describes the low-level details of the interface to a
    trigger function.  This information is only needed when writing a
    trigger function in C.  If you are using a higher-level function
    language then these details are handled for you. -->

    この節では、トリガ関数のインターフェイスの低レベルな詳細について説明します。この情報はトリガ関数をCで書く場合にのみ必要になります。もしもっと高レベルな関数言語を使っている場合はこれらの問題はすでに解決されています。
   </para>

    <note>
     <para>
      <!-- The interface described here applies for
      <productname>Postgres</productname> 7.1 and later.
      Earlier versions passed the TriggerData pointer in a global
      variable CurrentTriggerData. -->

      ここで説明されるインターフェイスは<productname>Postgres</productname> 7.1以降に当てはまります。それ以前のバージョンではTriggerDataポインタはグローバル変数CurrentTriggerDataの中で渡されていました。

     </para>
    </note>

   <para>
    <!-- 
    When a function is called by the trigger manager, it is not passed any
    normal parameters, but it is passed a "context" pointer pointing to a
    TriggerData structure.  C functions can check whether they were called
    from the trigger manager or not by executing the macro
    <literal>CALLED_AS_TRIGGER(fcinfo)</literal>, which expands to -->

    関数がトリガマネージャから呼び出されるときは、通常のどのようなパラメータに渡されるのではなくTriggerData構造体を指す「コンテキスト」ポインタに渡されます。C関数はトリガマネージャから呼び出されたのかどうかをマクロ<literal>CALLED_AS_TRIGGER(fcinfo)</literal>を実行することでチェックすることができ、下記のようになります。
</para>

<programlisting>
        ((fcinfo)->context != NULL &amp;&amp; IsA((fcinfo)->context, TriggerData))
</programlisting>

<para>
    <!-- 
    If this returns TRUE, then it is safe to cast fcinfo->context to type
    <literal>TriggerData *</literal> and make use of the pointed-to
    TriggerData structure.
    The function must <emphasis>not</emphasis> alter the TriggerData
    structure or any of the data it points to. -->

    もしこれがTRUEを返す場合、fcinfo->contextを<literal>TriggerData *</literal>型にキャストし、TriggerData構造体を指すものを利用することは安全です。その関数はTriggerData構造体やそれが指すどのようなデータも変えては<emphasis>いけません</emphasis>。

   </para>

   <para>
    <!-- 
    struct TriggerData is defined in src/include/commands/trigger.h:
    -->
    TriggerData構造体はsrc/include/commands/trigger.hの中で定義されています。<indexterm><primary>TriggerData</primary></indexterm><indexterm><primary sortas="こうぞうたい">構造体</primary><secondary>TriggerData</secondary></indexterm>
</para>

<programlisting>
typedef struct TriggerData
{
    NodeTag       type;
    TriggerEvent  tg_event;
    Relation      tg_relation;
    HeapTuple     tg_trigtuple;
    HeapTuple     tg_newtuple;
    Trigger      *tg_trigger;
} TriggerData;
</programlisting>

<para>
    <!-- where the members are defined as follows: -->
    メンバは下記のように定義されています。
</para>

    <variablelist>
     <varlistentry>
      <term>type</term>
      <listitem>
       <para>
        <!-- 
        Always <literal>T_TriggerData</literal> if this is a trigger event.
        -->
        もしこれがトリガイベントである場合、常に<literal>T_TriggerData</literal>です。

       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>tg_event</term>
      <listitem>
       <para>
        <!-- 
	describes the event for which the function is called. You may use the
	following macros to examine <literal>tg_event</literal>: -->

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

	<variablelist>
	 <varlistentry>
	  <term>TRIGGER_FIRED_BEFORE(tg_event)</term>
	  <listitem>
	   <para>
	    <!-- returns TRUE if trigger fired BEFORE. -->
            トリガがBEFOREで発行され場合TRUEを返します。<indexterm><primary>TRIGGER_FIRED_BEFORE</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>TRIGGER_FIRED_BEFORE</secondary></indexterm>
 
	   </para>
	  </listitem>
	 </varlistentry>

	 <varlistentry>
	  <term>TRIGGER_FIRED_AFTER(tg_event)</term>
	  <listitem>
	   <para>
	    <!-- Returns TRUE if trigger fired AFTER. -->
            トリガがAFTERを発行した場合TRUEを返します。<indexterm><primary>TRIGGER_FIRED_AFTER</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>TRIGGER_FIRED_AFTER</secondary></indexterm>

	   </para>
	  </listitem>
	 </varlistentry>

	 <varlistentry>
	  <term>TRIGGER_FIRED_FOR_ROW(event)</term>
	  <listitem>
	   <para>
	    <!-- Returns TRUE if trigger fired for
	    a ROW-level event. -->
            トリガが行レベルのイベントを発行した場合TRUEを返します。<indexterm><primary>TRIGGER_FIRED_FOR_ROW</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>TRIGGER_FIRED_FOR_ROW</secondary></indexterm>

	   </para>
	  </listitem>
	 </varlistentry>

	 <varlistentry>
	  <term>TRIGGER_FIRED_FOR_STATEMENT(event)</term>
	  <listitem>
	   <para>
	    <!-- Returns TRUE if trigger fired for
	    STATEMENT-level event. -->

            トリガがステートメントレベルのイベントを発行した場合TRUEを返します。<indexterm><primary>TRIGGER_FIRED_FOR_STATEMENT</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>TRIGGER_FIRED_FOR_STATEMENT</secondary></indexterm>

	   </para>
	  </listitem>
	 </varlistentry>

	 <varlistentry>
	  <term>TRIGGER_FIRED_BY_INSERT(event)</term>
	  <listitem>
	   <para>
	    <!-- Returns TRUE if trigger fired by INSERT. -->
            トリガがINSERTに発行された場合TRUEを返します。<indexterm><primary>TRIGGER_FIRED_BY_INSERT</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>TRIGGER_FIRED_BY_INSERT</secondary></indexterm>
	   </para>
	  </listitem>
	 </varlistentry>

	 <varlistentry>
	  <term>TRIGGER_FIRED_BY_DELETE(event)</term>
	  <listitem>
	   <para>
	    <!-- Returns TRUE if trigger fired by DELETE. -->
            トリガがDELETEに発行された場合TRUEを返します。 <indexterm><primary>TRIGGER_FIRED_BY_DELETE</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>TRIGGER_FIRED_BY_DELETE</secondary></indexterm>
	   </para>
	  </listitem>
	 </varlistentry>

	 <varlistentry>
	  <term>TRIGGER_FIRED_BY_UPDATE(event)</term>
	  <listitem>
	   <para>
	    <!-- Returns TRUE if trigger fired by UPDATE. -->
            トリガがUPDATEに発行された場合TRUEを返します。<indexterm><primary>TRIGGER_FIRED_BY_UPDATE</primary></indexterm><indexterm><primary sortas="とりが">トリガ</primary><secondary>TRIGGER_FIRED_BY_UPDATE</secondary></indexterm>

	   </para>
	  </listitem>
	 </varlistentry>
	</variablelist>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>tg_relation</term>
      <listitem>
       <para>
        <!-- 
	is a pointer to structure describing the triggered relation. Look at
	src/include/utils/rel.h for details about this structure.  The most
	interest things are tg_relation->rd_att (descriptor of the relation
	tuples) and tg_relation->rd_rel->relname (relation's name. This is not
	char*, but NameData.  Use SPI_getrelname(tg_relation) to get char* if
	you need a copy of name). -->

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

       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>tg_trigtuple</term>
      <listitem>
       <para>
        <!-- 
	is a pointer to the tuple for which the trigger is fired. This is 
        the tuple being inserted (if INSERT), deleted (if DELETE) or 
        updated (if UPDATE).
	If INSERT/DELETE then this is what you are to return to Executor if 
	you don't want to replace tuple with another one (INSERT) or skip the
	operation. -->

        トリガが発行されたタプルへのポインタです。これは挿入された（INSERTの場合）、削除された（DELETEの場合）、更新された（UPDATEの場合）タプルです。もしINSERT/DELETEの場合、タプルを別のもの（INSERT）で置き換えるか演算子を飛ばしたくなければ、これをエクゼキュータに返してください。
       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>tg_newtuple</term>
      <listitem>
       <para>
        <!-- 
	is a pointer to the new version of tuple if UPDATE and NULL if this is
	for an INSERT or a DELETE. This is what you are to return to Executor 
        if UPDATE and you don't want to replace this tuple with another one 
        or skip	the operation. -->

        もしこれがINSERTかDELETEのためでUPDATEかNULLならば、タプルの新しいバージョンのためのポインタです。もしUPDATEだった場合で別のタプルで置き換えるか演算を飛ばしたくなければ、これをエクゼキュータに返してください。

       </para>
      </listitem>
     </varlistentry>

     <varlistentry>
      <term>tg_trigger</term>
      <listitem>
       <para>
        <!--  
	is pointer to structure Trigger defined in src/include/utils/rel.h:
        -->
        src/include/utils/rel.hで定義された構造体Triggerへのポインタです。<indexterm><primary>Trigger</primary></indexterm><indexterm><primary sortas="こうぞうたい">構造体</primary><secondary>Trigger</secondary></indexterm>
</para>

<programlisting>
typedef struct Trigger
{
    Oid         tgoid;
    char       *tgname;
    Oid         tgfoid;
    FmgrInfo    tgfunc;
    int16       tgtype;
    bool        tgenabled;
    bool        tgisconstraint;
    bool        tgdeferrable;
    bool        tginitdeferred;
    int16       tgnargs;
    int16       tgattr[FUNC_MAX_ARGS];
    char      **tgargs;
} Trigger;
</programlisting>

<para>
	<!-- where
	tgname is the trigger's name, tgnargs is number of arguments in tgargs,
	tgargs is an array of pointers to the arguments specified in the CREATE
	TRIGGER statement. Other members are for internal use only.
        -->
        ここではtgnameがトリガの名前、tgnargsがtgargsの中の引数の数、tgargsはCREATE TRIGGER文で指定された引数へのポインタの配列です。他のメンバは内部使用のみです。

       </para>
      </listitem>
     </varlistentry>
    </variablelist>
  </sect1>

  <sect1 id="trigger-datachanges">
   <!-- <title>Visibility of Data Changes</title> -->
   <title>データ変更時の可視性</title>
<indexterm><primary sortas="とりが">トリガ</primary><secondary sortas="かしせい">可視性</secondary></indexterm>
   <para>
    <!-- 
    <productname>Postgres</productname> data changes visibility rule: 
    during a query execution, data changes made by the query itself 
    (via SQL-function, SPI-function, triggers)
    are invisible to the query scan.  For example, in query -->

    <productname>Postgres</productname>でのデータ変更時の可視規則は以下のとおりです。問い合わせの実行中、その問い合わせ自身によって（SQL関数、SPI関数、トリガ経由で）発生したデータの変更は、その問い合わせのスキャンに対して不可視となります。たとえば、以下のような問い合わせでは、挿入されたタプルは、SELECTスキャンに対して不可視となります。
</para>

<programlisting>
INSERT INTO a SELECT * FROM a;
</programlisting>

<para>
    <!--
    tuples inserted are invisible for SELECT scan.  In effect, this
    duplicates the database table within itself (subject to unique index
    rules, of course) without recursing. -->

    このため、この問い合わせはデータベースのテーブルを再帰処理することなく（もちろん、一意性インデックス規則に従って）二重化します。

   </para>

   <para>
    <!-- 
    But keep in mind this notice about visibility in the SPI documentation:
    -->

    しかし、SPI文書中の可視性に関する次の注意を覚えておいてください。
</para>

    <blockquote>
     <para>
<!--
Changes made by query Q are visible by queries that are started after
query Q, no matter whether they are started inside Q (during the
execution of Q) or after Q is done. -->

   問い合わせQによりなされる変更は、問い合わせQの後に開始した問い合わせに対しては可視です。この問い合わせがQの内側で（Qの実行中に）開始されたのか、Qの実行が終わってから開始されたのかについては問いません。

     </para>
    </blockquote>

   <para>
    <!--
    This is true for triggers as well so, though a tuple being inserted
    (tg_trigtuple) is not visible to queries in a BEFORE trigger, this tuple
    (just inserted) is visible to queries in an AFTER trigger, and to queries
    in BEFORE/AFTER triggers fired after this! -->

    当然のことですが、挿入されるタプル（tg_trigtuple）はBEFOREトリガ中の問い合わせに対しては不可視となります。AFTERトリガ中の問い合わせに対してはこの（挿入されたばかりの）タプルは可視に、そして、その後のBEFORE/AFTERトリガ中の問い合わせでは可視になります！

   </para>
  </sect1>

  <sect1 id="trigger-examples">
   <!-- <title>Examples</title> -->
   <title>例</title>
<indexterm><primary sortas="とりが">トリガ</primary><secondary sortas="れい">例</secondary></indexterm>
   <para>
    <!-- There are more complex examples in
    <filename>src/test/regress/regress.c</filename> and
    in <filename>contrib/spi</filename>. -->

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

   </para>

   <para>
    <!-- 
    Here is a very simple example of trigger usage.  Function trigf reports
    the number of tuples in the triggered relation ttest and skips the
    operation if the query attempts to insert NULL into x (i.e - it acts as a
    NOT NULL constraint but doesn't abort the transaction). -->

    トリガの使い方に関するとても簡単な例をここに示します。trigf関数はトリガを発行したttestリレーション内にあるタプル数を報告し、問い合わせがxにNULLを挿入しようとしていた場合はその操作をとばします（つまり、この関数は、トランザクションを中断させないNOT NULL制約のような動作をします）。
</para>

<programlisting>
<!--#include "executor/spi.h"	/* this is what you need to work with SPI */
#include "commands/trigger.h"	/* __ and triggers */-->
#include "executor/spi.h"       /* これは SPI を使用する場合に必要なものです */
#include "commands/trigger.h"   /* これは SPI とトリガの場合です */

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;

    <!-- /* Make sure trigdata is pointing at what I expect */ -->
        /* trigdata が期待どおりのものを指していることを確認します */

    if (!CALLED_AS_TRIGGER(fcinfo))
        elog(ERROR, "trigf: not fired by trigger manager");

    <!-- /* tuple to return to Executor */ -->
        /* エクゼキュータに返すタプル */

    if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
        rettuple = trigdata->tg_newtuple;
    else
        rettuple = trigdata->tg_trigtuple;
    
    <!-- /* check for NULLs ? */ -->
         /* NULLに関する点検を行うかどうか */

    if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event) &amp;&amp;
        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;

    <!-- /* Connect to SPI manager */ -->
        /* SPIマネージャに接続します */

    if ((ret = SPI_connect()) < 0)
        elog(NOTICE, "trigf (fired %s): SPI_connect returned %d", when, ret);

    <!-- /* Get number of tuples in relation */ -->
    /* リレーション中のタプル数を取得します */

    ret = SPI_exec("select count(*) from ttest", 0);

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

    i = SPI_getbinval(
           SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &amp;isnull);

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

    SPI_finish();

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

    return PointerGetDatum(rettuple);
}
</programlisting>

   <para>
    <!-- Now, compile and create the trigger function: -->
    ここでコンパイルを行い、トリガ関数を作ります。
</para>

<programlisting>
create function trigf () returns opaque as 
'...path_to_so' language 'C';

create table ttest (x int4);
</programlisting>

<programlisting>
vac=> <userinput>create trigger tbefore before insert or update or delete on ttest </userinput>
<userinput>for each row execute procedure trigf();</userinput>
CREATE
vac=> <userinput>create trigger tafter after insert or update or delete on ttest </userinput>
<userinput>for each row execute procedure trigf();</userinput>
CREATE
vac=> <userinput>insert into ttest values (null);</userinput>
NOTICE:trigf (fired before): there are 0 tuples in ttest
INSERT 0 0

<!-- Insertion skipped and AFTER trigger is not fired -->
挿入操作はとばされ、また、AFTERトリガも発行されません。

vac=> <userinput>select * from ttest;</userinput>
x
-
(0 rows)

vac=> <userinput>insert into ttest values (1);</userinput>
NOTICE:trigf (fired before): there are 0 tuples in ttest
NOTICE:trigf (fired after ): there are 1 tuples in ttest
                                       ^^^^^^^^
<!--                          remember what we said about visibility. -->
                           可視性について述べたことを思い出してください。
INSERT 167793 1
vac=> <userinput>select * from ttest;</userinput>
x
-
1
(1 row)

vac=> <userinput>insert into ttest select x * 2 from ttest;</userinput>
NOTICE:trigf (fired before): there are 1 tuples in ttest
NOTICE:trigf (fired after ): there are 2 tuples in ttest
                                       ^^^^^^^^
<!--                         remember what we said about visibility. -->
                           可視性について述べたことを思い出してください。

INSERT 167794 1
vac=> <userinput>select * from ttest;</userinput>
x
-
1
2
(2 rows)

vac=> <userinput>update ttest set x = null where x = 2;</userinput>
NOTICE:trigf (fired before): there are 2 tuples in ttest
UPDATE 0
vac=> <userinput>update ttest set x = 4 where x = 2;</userinput>
NOTICE:trigf (fired before): there are 2 tuples in ttest
NOTICE:trigf (fired after ): there are 2 tuples in ttest
UPDATE 1
vac=> <userinput>select * from ttest;</userinput>
x
-
1
4
(2 rows)

vac=> <userinput>delete from ttest;</userinput>
NOTICE:trigf (fired before): there are 2 tuples in ttest
NOTICE:trigf (fired after ): there are 1 tuples in ttest
NOTICE:trigf (fired before): there are 1 tuples in ttest
NOTICE:trigf (fired after ): there are 0 tuples in ttest
                                       ^^^^^^^^
<!--                          remember what we said about visibility. -->
                           可視性について述べたことを思い出してください。
DELETE 2
vac=> <userinput>select * from ttest;</userinput>
x
-
(0 rows)
</programlisting>

  </sect1>
 </chapter>

<!-- Keep this comment at the end of the file
Local variables:
mode:sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"./reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:("/usr/lib/sgml/catalog")
sgml-local-ecat-files:nil
End:
-->
