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

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

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

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

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

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.9.6を参照してください)。

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

独自の呼び出しハンドラを作成する際、標準配布物に含まれる手続き言語は優れたリファレンスです。 ソースツリーのsrc/plサブディレクトリを調べてください。