第 47章手続き言語ハンドラの作成

現在のコンパイル言語用"バージョン 1"インタフェース以外のある言語で作成された関数の呼び出しはすべて、特定の言語用の呼び出しハンドラを経由します。 (これには、ユーザ定義手続き言語で作成された関数、SQLで作成された関数、コンパイル言語用バージョン0インタフェースを使用した関数が含まれます。) 提供されたソーステキストを解釈するなどによって、関数の実行を意味のある方法で行なうことは、呼び出しハンドラの責任です。 この章では、どのように新しい手続き言語の呼び出しハンドラを作成できるかについて概要を示します。

手続き言語用の呼び出しハンドラは"通常"の関数で、Cなどのコンパイル言語で作成し、バージョン1インタフェースを使用し、引数をとらずにlanguage_handlerを返すものとしてPostgreSQLに登録しなければなりません。 この特殊な仮想型は、その関数が呼び出しハンドラとして識別し、SQLコマンド内で直接その関数が呼び出されることを防止します。

呼び出しハンドラは、他の関数と同じ方法で呼び出されます。 引数値と呼び出された関数についての情報を含むFunctionCallInfoData structのポインタを受取り、Datum型の結果を返すもの(そして、SQLのNULLという結果を返そうとする場合に、FunctionCallInfoData 構造体のisnullフィールドを設定するかもしれないもの)と想定されています。 呼び出しハンドラと通常の呼び出される関数との違いは、 FunctionCallInfoData構造体のflinfo->fn_oidに、呼び出しハンドラ自身ではなく、実際に呼び出される関数のOIDが含まれるという点です。 呼び出しハンドラはこのフィールドを使用して、どの関数を呼び出すのかを決定しなければなりません。 また、渡された引数リストは、呼び出しハンドラの宣言ではなく、目的とする関数の宣言に従うよう設定されています。

pg_proc システムテーブルから関数のエントリを取り出し、呼び出される関数の引数と戻り値の型を解析するまでを呼び出しハンドラが行ないます。 関数のCREATE FUNCTIONAS句は、pg_procの行のprosrc列にあります。 これは(PL/Tclなどの)手続き言語自体で作成されたソーステキストの場合や、ファイルへのパス名の場合や、何らかの呼び出しハンドラに詳細に何をすべきかを通知するものの場合があります。

1つのSQL文に対して、同じ関数が何回も呼び出されることがよくあります。 呼び出しハンドラは、flinfo->fn_extraフィールドを使用して、呼び出す関数に関する情報を繰返し検索することを防ぐことができます。 これは初期状態ではNULLですが、呼び出しハンドラによって呼び出す関数の情報を指すように設定することもできます。 その後の呼び出しでは、flinfo->fn_extraが非NULLであれば、それを使用して、情報検索の段階を省略することができます。 呼び出しハンドラはflinfo->fn_extraが少なくとも現在の問い合わせの終了まで有効なメモリを指しているかどうかを確認しなければなりません。 FmgrInfoデータ構造体は長く保持される可能性があるからです。 この方法の1つとして、 flinfo->fn_mcxtで指定されたメモリコンテキスト内に余分なデータを割り当てることです。 このデータは通常FmgrInfo 自身と同期間有効です。 しかし、ハンドラはまた、長時間メモリコンテキストにあるものを使用するかどうかを選ぶこともできます。 これにより、問い合わせを跨った、関数定義情報をキャッシュすることができます。

手続き言語関数がトリガとして呼び出された場合、引数は通常の方法では渡されず、FunctionCallInfoDatacontextフィールドが、普通の関数呼び出しのようにNULLにはならずに、TriggerData構造体を指しています。 呼び出しハンドラは、手続き言語に対しトリガ情報を取り出す機構を提供します。

以下は、Cで作成した手続き言語ハンドラの雛型です。

#include "postgres.h"
#include "executor/spi.h"
#include "commands/trigger.h"
#include "fmgr.h"
#include "access/heapam.h"
#include "utils/syscache.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"

PG_FUNCTION_INFO_V1(plsample_call_handler);

Datum
plsample_call_handler(PG_FUNCTION_ARGS)
{
    Datum          retval;

    if (CALLED_AS_TRIGGER(fcinfo))
    {
        /*
         * トリガ関数として呼び出された
         */
        TriggerData    *trigdata = (TriggerData *) fcinfo->context;

        retval = ...
    }
    else
    {
        /*
         * 関数として呼び出された
         */

        retval = ...
    }

    return retval;
}

数千行のコードを上のドットの代わりに追加するだけで、呼び出しハンドラを完成することができます。

ハンドラ関数を動的読み込み可能なモジュールにコンパイルした後、以下のコマンドでサンプルの手続き言語を登録することができます。(項33.7.6を参照して下さい。)

CREATE FUNCTION plsample_call_handler() RETURNS language_handler
    AS 'filename'
    LANGUAGE C;
CREATE LANGUAGE plsample
    HANDLER plsample_call_handler;