C言語関数

PostgreSQL 9.1.1文書
前のページ | 巻戻し | 第 35章SQLの拡張 | 早送り | 次のページ

# 35.9. C言語関数

ユーザ定義の関数はC（もしくはC++のようなCと互換性のある言語）で作成することができます。 そのような関数は動的ロード可能オブジェクト（共有ライブラリとも呼ばれます）としてコンパイルされ、必要に応じてサーバにロードされます。 動的ロード機能が、"C言語"関数を"内部"関数と区別するものです。 コーディング方法は基本的に両方とも同じです。 （したがって、標準内部関数ライブラリはユーザ定義のC関数のコーディング例の豊富な情報源となります。）

現在、2つの異なる呼び出し規約がC関数で使用されています。 より新しい"Version 1"呼び出し規約は、以下に示すように、その関数用に呼び出しマクロ`PG_FUNCTION_INFO_V1()`を書くことで示されます。 このマクロが存在しなければ、旧形式（"Version 0"）の関数であることを示します。 どちらの場合も`CREATE FUNCTION`で指定する言語名は`C`です。 旧形式の関数は移植性の問題と機能の不足のために勧められません。 これは現在、互換性の理由のために存在しています。

## 35.9.1. 動的ロード

特定のロード可能オブジェクト内のユーザ定義の関数がセッションで最初に呼び出されると、動的ローダは、その関数を呼び出すことができるように、オブジェクトファイルをメモリ内に読み込みます。 そのため、ユーザ定義のC関数用の`CREATE FUNCTION`はその関数について、ロード可能オブジェクトファイルの名前とオブジェクトファイル中の呼び出される特定の関数のC名称（リンクシンボル）という2つの情報を指定しなければなりません。 C名称が明示的に指定されなかった場合、SQLにおける関数名と同じものと仮定されます。

`CREATE FUNCTION`コマンドで与えられた名前に基づいて、共有オブジェクトファイルの場所を見つける際に以下のアルゴリズムが使用されます。

1. 名前が絶対パスの場合、指定されたファイルが読み込まれます。

2. 名前が`$libdir`という文字列から始まる場合、その部分はPostgreSQLパッケージのライブラリディレクトリで置き換えられます。 このディレクトリはビルド時に決定されます。

3. 名前にディレクトリ部分がない場合、そのファイルはdynamic_library_path設定変数で指定されたパス内から検索されます。

4. 上記以外の場合（ファイルがパス内に存在しない場合や相対ディレクトリ部分を持つ場合）、動的ローダは指定された名前をそのまま使用し、ほとんどの場合は失敗します。 （これは現在の作業ディレクトリに依存するため信頼できません。）

ここまでの流れがうまくいかなかった場合、プラットフォーム独自の共有ライブラリファイル拡張子（多くの場合`.so`）が指定された名前に追加され、再度この流れを試みます。 同様に失敗した場合は、読み込みは失敗します。

共有ライブラリを`$libdir`から相対的に、もしくは動的ライブラリパスの通った所に配置することを推奨します。 異なる場所に新しいインストレーションを配置する場合にバージョンアップを簡単にします。 `$libdir`が示す実際のディレクトリは`pg_config --pkglibdir`コマンドを使用することでわかります。

PostgreSQLサーバの実効ユーザIDはロード予定のファイルのパスまで到達できなければなりません。 よくある失敗として、postgresユーザに対して読み込み、実行、または両方の権限がそのファイルとその上位ディレクトリに与えられていないことがあります。

どの場合でも、`CREATE FUNCTION`コマンドに与えたファイル名はそのままシステムカタログに保存されます。 ですので、もしそのファイルを再度読み込む必要がある場合、同じ処理が適用されます。

> 注意: PostgreSQLはC関数を自動的にコンパイルしません。 `CREATE FUNCTION`コマンドで参照する前に、そのオブジェクトファイルはコンパイルされていなければなりません。 さらなる情報については項35.9.6を参照してください。

確実に、動的にロードされるモジュールが互換性がないサーバにロードされないように、PostgreSQLは、そのファイルに適切な内容を持つ"魔法のブロック"が含まれているかどうか検査します。 これによりサーバは、メジャーバージョンが異なるPostgreSQL用にコンパイルされたモジュールなど、明確に互換性がないことを検知することができます。 魔法のブロックはPostgreSQL 8.2から要求されています。 魔法のブロックを含めるためには、以下をモジュールのソースファイルに一度（一度だけ）、`fmgr.h`ヘッダファイルをincludeさせた後で、記述してください。

    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif

そのコードをリリース8.2より前のPostgreSQL用にコンパイルする必要がなければ、`#ifdef`テストを省略することができます。

最初に使用された後も、動的にロードされたオブジェクトファイルはメモリ内に保持されます。 同一セッションにおいてそのファイル内の関数をその後に呼び出した場合、シンボルテーブルの検索に要する小さなオーバーヘッドしかかかりません。 例えば再コンパイルした後など、そのオブジェクトファイルを強制的に再度読み込ませる必要がある場合は、新しいセッションを開始してください。

省略することもできますが、動的にロードされるファイルに初期化処理関数と最終処理関数を含めることができます。 `_PG_init`という関数がファイルに存在すると、この関数はファイルがロードされた直後に呼び出されます。 この関数は引数を取らずvoid型を返さなければなりません。 `_PG_fini`という関数がファイルに存在すると、この関数はファイルがアンロードされる直前に呼び出されます。 この関数も同様に引数を取らずvoid型を返さなければなりません。 `_PG_fini`がファイルのアンロード時にのみ呼び出されるものであり、処理の終了時に呼び出されるものではないことに注意してください。 （現在、アンロードは無効となっていますので、決して発生しません。将来変更される可能性があります。）

## 35.9.2. C言語関数における基本型

C言語関数の作成方法を理解するためには、PostgreSQLが基本データ型を内部でどのように表現し、どのようにそれらを関数とやり取りしているかを理解する必要があります。 内部的にPostgreSQLは基本型を"メモリの小さな塊"とみなします。 ある型を定義するユーザ定義関数は、言い換えると、PostgreSQLがそれを操作できる方法を定義します。 つまり、PostgreSQLはデータの格納、ディスクからの取り出しのみを行い、データの入力や処理、出力にはユーザ定義関数を使用します。

基本型は下記の3つのいずれかの内部書式を使用しています。

- 固定長の値渡し

- 固定長の参照渡し

- 可変長の参照渡し

値渡しは、1、2、4バイト長の型のみで使用することができます（使用するマシンの`sizeof(Datum)`が8の場合は8バイトも使用できます）。 データ型を定義する際、その型がすべてのアーキテクチャにおいて同一の大きさ（バイト数）となるように定義するように注意してください。 例えば、`long`型はマシンによっては4バイトであったり、8バイトであったりして危険ですが、`int`型はほとんどのUnixマシンでは4バイトです。 Unixマシンにおける`int4`の理論的な実装は以下のようになります。

    /* 4 バイト整数、値渡し */
    typedef int int4;

一方、任意の大きさの固定長の型は参照として引き渡すことができます。 例として以下にPostgreSQLの型の実装サンプルを示します。

    /* 16 バイト構造体、参照渡し */
    typedef struct
    {
        double  x, y;
    } Point;

それらの型のポインタのみがPostgreSQL関数の入出力時に使用できます。 それらの型の値を返すためには、`palloc()`を使用して正しい大きさのメモリ領域を割り当て、そのメモリ領域に値を入力し、それのポインタを返します。 （また、入力引数の1つと同じ型かつ同じ値を返したいのであれば、`palloc`を行う手間を省くことができます。 この場合は入力値へのポインタを単に返してください。）

最後に、すべての可変長型は参照として引き渡す必要があります。 また、すべての可変長型は正確に4バイトのlengthフィールドから始まる必要があり、その型に格納されるすべてのデータはlengthフィールドのすぐ後のメモリ領域に置かれる必要があります。 lengthフィールドにはその構造体の総長が格納されます。つまり、lengthフィールドそのものもその大きさに含まれます。

この他の重要な点は、データ型の値の中で初期化されていないビットを残さないことです。 例えば、構造体内に存在する可能性がある整列用のパディングバイトを注意してすべてゼロクリアしてください。 こうしないと、独自データ型の論理的に等価な定数がプランナにより一致しないものと判断され、（不正確ではありませんが）非効率的な計画をもたらすかもしれません。

警告
参照渡しの入力値の内容を決して変更しないでください。 指定したポインタがディスクバッファを直接指し示している可能性がよくありますので、変更すると、ディスク上のデータを破壊してしまうかもしれません。 この規則の唯一の例外について項35.10で説明します。

例えば、`text`型を定義するには、下記のように行えます。

    typedef struct {
        int4 length;
        char data[1];
    } text;

ここで宣言されたdataフィールドは、明らかにすべての取り得る文字列を保持できる長さではありません。 C言語では可変長の構造体を定義することは不可能ですので、Cコンパイラは配列の添字の範囲検査を行わないという事実に依存します。 必要な領域量を割り当て、あたかも正しい長さで宣言されたかのように、配列としてアクセスするだけです。 （この手法はよく使用されます。C言語に関する多くの書籍で説明されています。）

可変長型を操作する時、正確な大きさのメモリを割り当て、lengthフィールドを正確に設定することに注意する必要があります。 例えば、40バイトを`text`構造体に保持させたい場合、下記のようなコードを使用します。

    #include "postgres.h"
    ...
    char buffer[40]; /* our source data */
    ...
    text *destination = (text *) palloc(VARHDRSZ + 40);
    SET_VARSIZE(destination, VARHDRSZ + 40);
    memcpy(destination->data, buffer, 40);
    ...

`VARHDRSZ`は`sizeof(int4)`と同一ですが、可変長型のオーバーヘッド分の大きさを参照する時には、`VARHDRSZ`マクロを使用する方が好ましい形式とみなされています。 また長さフィールドを単なる代入ではなく`SET_VARSIZE`マクロを使用して設定しなければなりません。

表35-1は、PostgreSQLの組み込み型を使用するC言語関数を作成する時の、Cの型とSQL型との対応を規定したものです。 "定義場所"列では、型定義を取り出すためにインクルードしなければならないヘッダファイルを示しています。 （実際の定義は一覧中のファイルからインクルードされた、別のファイルであるかもしれません。 ユーザは定義されたインタフェースを厳守することを推奨されています。） `postgres.h`には必ず必要になる多くのものが宣言されていますので、ソースファイルの中で必ず初めにこのファイルをインクルードしなければならないことに注意してください。

表 35-1. 組み込みSQL型に相当するCの型

SQL型 | C 言語型 | 定義場所
--- | --- | ---
`abstime` | `AbsoluteTime` | `utils/nabstime.h`
`boolean` | `bool` | `postgres.h`（コンパイラで組み込み済みの可能性があります）
`box` | `BOX*` | `utils/geo_decls.h`
`bytea` | `bytea*` | `postgres.h`
`"char"` | `char` | （コンパイラで組み込み済み）
`character` | `BpChar*` | `postgres.h`
`cid` | `CommandId` | `postgres.h`
`date` | `DateADT` | `utils/date.h`
`smallint` (`int2`) | `int2` or `int16` | `postgres.h`
`int2vector` | `int2vector*` | `postgres.h`
`integer` (`int4`) | `int4` or `int32` | `postgres.h`
`real` (`float4`) | `float4*` | `postgres.h`
`double precision` (`float8`) | `float8*` | `postgres.h`
`interval` | `Interval*` | `utils/timestamp.h`
`lseg` | `LSEG*` | `utils/geo_decls.h`
`name` | `Name` | `postgres.h`
`oid` | `Oid` | `postgres.h`
`oidvector` | `oidvector*` | `postgres.h`
`path` | `PATH*` | `utils/geo_decls.h`
`point` | `POINT*` | `utils/geo_decls.h`
`regproc` | `regproc` | `postgres.h`
`reltime` | `RelativeTime` | `utils/nabstime.h`
`text` | `text*` | `postgres.h`
`tid` | `ItemPointer` | `storage/itemptr.h`
`time` | `TimeADT` | `utils/date.h`
`time with time zone` | `TimeTzADT` | `utils/date.h`
`timestamp` | `Timestamp*` | `utils/timestamp.h`
`tinterval` | `TimeInterval` | `utils/nabstime.h`
`varchar` | `VarChar*` | `postgres.h`
`xid` | `TransactionId` | `postgres.h`

ここまでで基本型に関してあり得る構造体のすべてを記述しましたので、実際の関数の例をいくつか示すことができます。

## 35.9.3. Version 0 呼び出し規約

まず最初に、現在は非推奨ですが理解しやすいので、"古いスタイル"の呼び出し規約を説明します。 Version-0メソッドでは、C関数の引数と結果は、通常のCのプログラムの記述の方法と同じような形式で行いますが、上記の説明のように、各SQLのデータ型に対するC言語での表現を注意して使用してください。

以下にいくつか例を示します。

    #include "postgres.h"
    #include <string.h>
    #include "utils/geo_decls.h"

    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif

    /* 値渡し */

    int
    add_one(int arg)
    {
        return arg + 1;
    }

    /* 固定長の参照渡し */

    float8 *
    add_one_float8(float8 *arg)
    {
        float8    *result = (float8 *) palloc(sizeof(float8));

        *result = *arg + 1.0;

        return result;
    }

    Point *
    makepoint(Point *pointx, Point *pointy)
    {
        Point     *new_point = (Point *) palloc(sizeof(Point));

        new_point->x = pointx->x;
        new_point->y = pointy->y;

        return new_point;
    }

    /* 可変長の参照渡し */

    text *
    copytext(text *t)
    {
        /*
         * VARSIZEは構造体の総長をバイト数で表したものです。
         */
        text *new_t = (text *) palloc(VARSIZE(t));
        SET_VARSIZE(new_t, VARSIZE(t));
        /*
         * VARDATAは構造体のデータ領域へのポインタです。
         */
        memcpy((void *) VARDATA(new_t), /* コピー先 */
               (void *) VARDATA(t),     /* コピー元 */
               VARSIZE(t) - VARHDRSZ);  /* バイト数 */
        return new_t;
    }

    text *
    concat_text(text *arg1, text *arg2)
    {
        int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
        text *new_text = (text *) palloc(new_text_size);

        SET_VARSIZE(new_text, new_text_size);
        memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
        memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
               VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
        return new_text;
    }

上のコードが`funcs.c`というファイルに用意され、共有オブジェクトとしてコンパイル済みであるとすると、以下のようなコマンドでPostgreSQLの関数を定義することができます。

    CREATE FUNCTION add_one(integer) RETURNS integer
         AS 'DIRECTORY/funcs', 'add_one'
         LANGUAGE C STRICT;

    -- "add_one"というSQL関数名をオーバーロードしていることに注意
    CREATE FUNCTION add_one(double precision) RETURNS double precision
         AS 'DIRECTORY/funcs', 'add_one_float8'
         LANGUAGE C STRICT;

    CREATE FUNCTION makepoint(point, point) RETURNS point
         AS 'DIRECTORY/funcs', 'makepoint'
         LANGUAGE C STRICT;

    CREATE FUNCTION copytext(text) RETURNS text
         AS 'DIRECTORY/funcs', 'copytext'
         LANGUAGE C STRICT;

    CREATE FUNCTION concat_text(text, text) RETURNS text
         AS 'DIRECTORY/funcs', 'concat_text'
         LANGUAGE C STRICT;

ここで、`DIRECTORY`は共有ライブラリファイルのディレクトリ（例えば、本節で使用する例のコードが含まれるPostgreSQLチュートリアルディレクトリ）を表します。 （`AS`句中では単に`'funcs'`を使用し、後で`DIRECTORY`を検索パスに追加する方がより良い方法です。 どの場合でも、一般的に`.so`や`.sl`が使用される、共有ライブラリ用のシステム独特の拡張子を省略することができます。）

ここで、関数を"厳密（strict）"と指定していることに注目してください。 これは、もし入力された値がNULLであった場合に、システムが自動的に返り値もNULLであるとみなすことを意味します。 これを行うことによって、関数のコードで入力値がNULLであるかどうかの検査を行う必要がなくなります。 これがなければ、参照渡し引数それぞれに対してヌルポインタについての検査を行うなど、NULL値の明示的な検査を行う必要性が出てきます。 （値渡し引数に関しては、検査を行う方法は存在しません。）

この呼び出し規約は単純ですが、この方法は移植性の面であまり優れていません。 この方法で`int`型より小さいデータ型を渡す部分で問題を抱えているアーキテクチャも存在します。 また、関数の結果としてNULLを返す簡単な方法はありません。 その上、NULL引数をうまく処理する方法としては、関数を厳密なものにする以外方法はありません。 次に説明するVersion-1規約ではこれらの問題が解決されています。

## 35.9.4. Version 1 呼び出し規約

Version-1呼び出し規約では、引数と結果の引き渡しの複雑さをなくすためにマクロを使用しています。 Version-1関数のC言語宣言は必ず下記のように行います。

    Datum funcname(PG_FUNCTION_ARGS)

さらに、マクロ呼び出し

    PG_FUNCTION_INFO_V1(funcname);

が同じソースファイルに書かれている必要があります。 （一般には、関数の直前に書かれます。） PostgreSQLではすべての内部関数はVersion-1であると認識するので、このマクロの呼び出しは`internal`言語関数では必要ありません。 しかし、動的にロードされる関数では必要です。

Version-1関数では、それぞれの実引数は、引数のデータ型に合った`PG_GETARG_`xxx`()`マクロを使用して取り出され、結果は戻り値の型に合った`PG_RETURN_`xxx`()`マクロを使用して返されます。 `PG_GETARG_`xxx`()`は、その引数として、取り出す関数引数の番号（ゼロから始まります）を取ります。 `PG_RETURN_`xxx`()`は、その引数として、実際に返す値を取ります。

上記と同じ関数をVersion-1形式で記述したものを以下に示します。

    #include "postgres.h"
    #include <string.h>
    #include "fmgr.h"
    #include "utils/geo_decls.h"

    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif

    /* 値渡し */

    PG_FUNCTION_INFO_V1(add_one);

    Datum
    add_one(PG_FUNCTION_ARGS)
    {
        int32   arg = PG_GETARG_INT32(0);

        PG_RETURN_INT32(arg + 1);
    }

    /* 固定長の参照渡し */

    PG_FUNCTION_INFO_V1(add_one_float8);

    Datum
    add_one_float8(PG_FUNCTION_ARGS)
    {
        /* FLOAT8用のマクロは参照渡しという性質を隠します */
        float8   arg = PG_GETARG_FLOAT8(0);

        PG_RETURN_FLOAT8(arg + 1.0);
    }

    PG_FUNCTION_INFO_V1(makepoint);

    Datum
    makepoint(PG_FUNCTION_ARGS)
    {
        /* ここのPoint型の参照渡しという性質は隠されていません */
        Point     *pointx = PG_GETARG_POINT_P(0);
        Point     *pointy = PG_GETARG_POINT_P(1);
        Point     *new_point = (Point *) palloc(sizeof(Point));

        new_point->x = pointx->x;
        new_point->y = pointy->y;

        PG_RETURN_POINT_P(new_point);
    }

    /* 可変長の参照渡し */

    PG_FUNCTION_INFO_V1(copytext);

    Datum
    copytext(PG_FUNCTION_ARGS)
    {
        text     *t = PG_GETARG_TEXT_P(0);
        /*
         * VARSIZEは構造体の総長をバイト数で表したものです。
         */
        text     *new_t = (text *) palloc(VARSIZE(t));
        SET_VARSIZE(new_t, VARSIZE(t));
        /*
         * VARDATAは構造体のデータ領域へのポインタです。
         */
        memcpy((void *) VARDATA(new_t), /* コピー先 */
               (void *) VARDATA(t),     /* コピー元 */
               VARSIZE(t) - VARHDRSZ);  /* バイト数 */
        PG_RETURN_TEXT_P(new_t);
    }

    PG_FUNCTION_INFO_V1(concat_text);

    Datum
    concat_text(PG_FUNCTION_ARGS)
    {
        text  *arg1 = PG_GETARG_TEXT_P(0);
        text  *arg2 = PG_GETARG_TEXT_P(1);
        int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
        text *new_text = (text *) palloc(new_text_size);

        SET_VARSIZE(new_text, new_text_size);
        memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
        memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
               VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
        PG_RETURN_TEXT_P(new_text);
    }

`CREATE FUNCTION`コマンドはVersion-0と同じものです。

一見Version-1のコーディング規約は無意味なものに見えるかもしれません。 しかし、マクロが必要のない情報を隠蔽しているので、多数の改良が行われています。 例えば、`add_one_float8`のコードでは、`float8`が参照渡しであることを意識する必要がなくなっています。 また別の例としては、可変長型の`GETARG`マクロは"TOASTされた"（圧縮または行外）値をより効率的に取り出すことができます。

Version-1関数の1つの大きな改善点は、NULLの入力/結果の処理能力です。 `PG_ARGISNULL(`n`)`マクロにより関数は各入力がNULLであるかどうかの検査を行うことができます。 （もちろんこれは、"厳密"と宣言されていない関数でのみ必要です。） `PG_GETARG_`xxx`()`マクロと同様、入力引数の番号はゼロから始まります。 引数がNULLでないことを確認するまでは、`PG_GETARG_`xxx`()`の実行は控えなければなりません。 結果としてNULLを返す場合は、`PG_RETURN_NULL()`を実行します。 これは、厳密な関数と厳密でない関数の両方で使用可能です。

新しい形式のインタフェースでは、その他のオプションとして`PG_GETARG_`xxx`()`マクロの変形を2つ提供しています。 1つ目の`PG_GETARG_`xxx`_COPY()`によって、安全に書き込むことができる指定引数のコピーが確実に返されます。 （通常のマクロは、物理的にテーブルに格納されている値へのポインタを返すことがあるので、書き込んではなりません。 `PG_GETARG_`xxx`_COPY()`マクロの結果は書き込み可能であることが保証されています。） 2つ目の変形は、引数を3つ取る`PG_GETARG_`xxx`_SLICE()`マクロからなります。 1つ目は関数の引数の番号（上記の通り）です。 2つ目と3つ目は、オフセットと返されるセグメントの長さです。 オフセットはゼロから始まり、負の長さは残りの値を返すことを要求します。 これらのマクロを使用すると、ストレージ種類が"external"（外部）である大きな値の一部へアクセスする際に非常に効果的です。 （列のストレージ種類は`ALTER TABLE `tablename` ALTER COLUMN `colname` SET STORAGE `storagetype``を使用して指定できます。 `storagetype`は、`plain`、`external`、`extended`、または`main`のいずれかです。）

最後に、Version-1関数呼び出し規約では、結果集合（項35.9.9）を返すこと、およびトリガ関数（第36章）と手続型言語の呼び出しハンドラ（第49章）を実装することができます。 また、Version-1コードは、標準Cの関数呼び出しプロトコルの制約を守りますので、Version-0よりも移植性があります。 詳細についてはソース配布物内の`src/backend/utils/fmgr/README`を参照してください。

## 35.9.5. コードの作成

より先進的な話題に入る前に、PostgreSQL C言語関数のコーディングについての規則をいくつか説明します。 C言語以外の言語で記述した関数をPostgreSQLに組み込みむことは可能であるかもしれませんが、例えばC++、FORTRANやPascalといった言語はC言語と同じ呼び出し規約に従いませんので、多くの場合、（可能であったとしても）困難です。 それはつまり、他の言語では同じ方法で関数に引数を渡したり、関数から結果を返すことを行わないということです。 このため、C言語関数は実際にC言語で書かれているものと仮定します。

C関数の作成と構築の基本規則を以下に示します。

- `pg_config --includedir-server`を使用して、使用中のシステム（もしくはユーザが実行するシステム）にてPostgreSQLサーバのヘッダファイルがインストールされた場所を見つけます。

- PostgreSQLに動的にロードできるように独自コードをコンパイル/リンクする時には常に、特別なフラグが必要となります。 特定のオペレーティングシステムにおけるコンパイル/リンク方法については項35.9.6を参照してください。

- 忘れずに項35.9.1で説明した"魔法のブロック"を共有ライブラリで定義してください。

- メモリを割り当てる際、Cライブラリの`malloc`と`free`ではなく、PostgreSQLの`palloc`と`pfree`を使用してください。 `palloc`で割り当てられたメモリは各トランザクションの終わりに自動的に解放され、メモリリークを防ぎます。

- `memset`を使用して、構造体を必ずゼロクリアしてください（または最初の段階で`palloc0`を用いて割り当ててください）。 構造体の各フィールドを割り当てたとしても、ゴミの値を持つ整列用のパディング（構造体内の穴）があるかもしれません。 こうしないと、ハッシュインデックスやハッシュ結合をサポートすることが困難です。 ハッシュを計算するには、データ構造体内の有意なビットのみを取り出す必要があるためです。 プランナはまた時折ビット単位の等価性を用いて定数の比較を行います。 このため論理的にな値がビット単位で等価でない場合に望まない計画になってしまう可能性があります。

- ほとんどのPostgreSQLの内部型は`postgres.h`に宣言されています。 一方、関数管理インタフェース（`PG_FUNCTION_ARGS`など）は`fmgr.h`で宣言されています。 したがって、少なくともこの2つのファイルをインクルードする必要があります。 移植性に関する理由により、`postgres.h`をその他のシステムヘッダファイル、ユーザヘッダファイルよりも先にインクルードしておくことが最善です。 `postgres.h`をインクルードすることは`elog.h`、`palloc.h`もインクルードすることになります。

- オブジェクトファイルで定義されているシンボル名は、互いに、またはPostgreSQLサーバの実行ファイルで定義されているものと異なっている必要があります。 これに関するエラーが表示される場合は、関数名または変数名を変更する必要があります。

## 35.9.6. 動的にロードされる関数のコンパイルとリンク

Cで書かれたPostgreSQLの拡張関数を使うためには、サーバが動的にロードできるように特別な方法でコンパイルとリンクを行う必要があります。 正確には共有ライブラリを作る必要があります。

本節の説明以上の詳しい情報はオペレーティングシステムのドキュメント、特にCコンパイラ`cc`とリンクエディタ`ld`のマニュアルページを参照してください。 さらに、PostgreSQLのソースコードの`contrib`ディレクトリにいくつか実例があります。 しかし、もしこれらの例に頼るとPostgreSQLソースコードが利用できることに依存したモジュールが作られてしまいます。

共有ライブラリの作成は一般的に実行プログラムのリンクに類似しています。 まずソースファイルがオブジェクトファイルにコンパイルされ、そのオブジェクトファイル同士がリンクされます。 これらのオブジェクトファイルは位置独立なコード（PIC）として作られる必要があります。 それは概念的には、実行プログラムから呼び出される時にメモリの適当な場所に置くことができるということです （実行プログラム用として作られたオブジェクトファイルはそのようにはコンパイルされません）。 共有ライブラリをリンクするコマンドは実行プログラムのリンクと区別するための特別なフラグがあります（少なくとも理論上ではそのようになっています。他のシステムではもっと醜い実際が見受けられます)。

次の例ではソースコードは`foo.c`ファイルにあると仮定し、`foo.so`という共有ライブラリを作るとします。 中間のオブジェクトファイルは特別な記述がない限り`foo.o`と呼ばれます。 共有ライブラリは1つ以上のオブジェクトファイルを持つことができますが、ここでは1つしか使いません。

BSD/OS

  PICを作るためのコンパイラフラグは`-fpic`です。 共有ライブラリを作るリンカフラグは`-shared`です。

      gcc -fpic -c foo.c
      ld -shared -o foo.so foo.o

  これはBSD/OSのバージョン4.0に適用されます。

FreeBSD

  PICを作るためのコンパイラフラグは`-fpic`です。 共有ライブラリを作るコンパイラフラグは`-shared`です。

      gcc -fpic -c foo.c
      gcc -shared -o foo.so foo.o

  これはFreeBSDのバージョン3.0に適用されます。

HP-UX

  PICを作るためのシステムコンパイラのコンパイラフラグは`+z`です。 GCCを使う場合は`-fpic`です。 共有ライブラリのためのリンカフラグは`-b`です。 したがって、以下のようになります。

      cc +z -c foo.c

  または

      gcc -fpic -c foo.c

  そして

      ld -b -o foo.sl foo.o

  HP-UXは他のほとんどのシステムと異なり共有ライブラリに`.sl`という拡張子を使います。

IRIX

  PICがデフォルトで、特別なコンパイラオプションは何も必要ありません。 共有ライブラリを作るためのリンカオプションは`-shared`です。

      cc -c foo.c
      ld -shared -o foo.so foo.o

Linux

  PICを作るためのコンパイラフラグは`-fpic`です。 いくつかのプラットフォームでは状況によって`-fpic`が動作しない場合は`-fPIC`を使わなければいけません。 さらに詳しい情報についてはGCCのマニュアルを参照してください。 共有ライブラリを作るコンパイラフラグは`-shared`です。 完全な例は下記のようになります。

      cc -fpic -c foo.c
      cc -shared -o foo.so foo.o

Mac OS X

  例を以下に示します。 開発者用ツールがインストールされていることが前提です。

      cc -c foo.c
      cc -bundle -flat_namespace -undefined suppress -o foo.so foo.o

NetBSD

  PICを作るためのコンパイラフラグは`-fpic`です。 ELFシステムでは`-shared`コンパイラフラグを使用して共有ライブラリをリンクします。 より古い非ELFシステムでは`ld -Bshareable`が使われます。

      gcc -fpic -c foo.c
      gcc -shared -o foo.so foo.o

OpenBSD

  PICを作成するためのコンパイラフラグは`-fpic`です。 共有ライブラリをリンクするには`ld -Bshareable`を使用します。

      gcc -fpic -c foo.c
      ld -Bshareable -o foo.so foo.o

Solaris

  PICを作るためのコンパイラフラグはSunコンパイラでは`-KPIC`で、GCCでは`-fpic`です。 共有ライブラリをリンクするためには、どちらのコンパイラでもコンパイラオプションは`-G`で、GCCの場合、代わりに`-shared`オプションを使うこともできます。

      cc -KPIC -c foo.c
      cc -G -o foo.so foo.o

  もしくは

      gcc -fpic -c foo.c
      gcc -G -o foo.so foo.o

Tru64 UNIX

  PICはデフォルトで、コンパイルコマンドは通常のものです。 リンクのためには特別なオプション付きの`ld`を使用します。

      cc -c foo.c
      ld -shared -expect_unresolved '*' -o foo.so foo.o

  システムのコンパイラではなくGCCを使う場合も同じ手順です。 特別のオプションは必要ありません。

UnixWare

  PICを作るためのコンパイラフラグはSCOコンパイラでは`-KPIC`で、GCCでは`-fpic`です。 共有ライブラリのリンクは、SCOコンパイラではコンパイラオプションは`-G`で、GCCでは`-shared`です。

      cc -K PIC -c foo.c
      cc -G -o foo.so foo.o

  もしくは

      gcc -fpic -c foo.c
      gcc -shared -o foo.so foo.o

> ティップ: これがあまりに難しいようであれば、GNU Libtoolの使用を検討すべきです。 これはプラットフォームの違いを、統一されたインタフェースで判らないようにします。

これで完成した共有ライブラリファイルはPostgreSQLにロードすることができます。 `CREATE FUNCTION`コマンドにファイル名を指定する時には、中間オブジェクトファイルではなく共有ライブラリファイル名の名前を与えてください。 システムの標準共有ライブラリ用の拡張子（通常`.so`あるいは`.sl`）は`CREATE FUNCTION`で省略することができ、そして移植性を最も高くするため通常は省略されます。

サーバがライブラリファイルをどこに見つけるかに関しては項35.9.1を見直してください。

## 35.9.7. 複合型引数

複合型ではCの構造体のような固定のレイアウトがありません。 複合型のインスタンスはNULLフィールドを持つことができます。 さらに、複合型で継承階層の一部であるものは、同じ継承階層の他のメンバとは異なるフィールドを持つこともできます。 そのため、PostgreSQLはC言語から複合型のフィールドにアクセスするための関数インタフェースを提供します。

以下のような問い合わせに答える関数を書こうとしていると仮定します。

    SELECT name, c_overpaid(emp, 1500) AS overpaid
        FROM emp
        WHERE name = 'Bill' OR name = 'Sam';

Version 0呼び出し規約を使用すると、`c_overpaid`は以下のように定義できます。

    #include "postgres.h"
    #include "executor/executor.h"  /* GetAttributeByName()用 */

    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif

    bool
    c_overpaid(HeapTupleHeader t, /* empの現在の行 */
               int32 limit)
    {
        bool isnull;
        int32 salary;

        salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
        if (isnull)
            return false;
        return salary > limit;
    }

Version 1で作成すると、上の関数は以下のようになります。

    #include "postgres.h"
    #include "executor/executor.h"  /* GetAttributeByName()用 */

    #ifdef PG_MODULE_MAGIC
    PG_MODULE_MAGIC;
    #endif

    PG_FUNCTION_INFO_V1(c_overpaid);

    Datum
    c_overpaid(PG_FUNCTION_ARGS)
    {
        HeapTupleHeader  t = PG_GETARG_HEAPTUPLEHEADER(0);
        int32            limit = PG_GETARG_INT32(1);
        bool isnull;
        Datum salary;

        salary = GetAttributeByName(t, "salary", &isnull);
        if (isnull)
            PG_RETURN_BOOL(false);
        /* この他、salaryがNULLの場合用にPG_RETURN_NULL()を行った方が良いでしょう */

        PG_RETURN_BOOL(DatumGetInt32(salary) > limit);
    }

`GetAttributeByName`は、指定された行から属性を返す、PostgreSQLシステム関数です。 これには3つの引数があります。 それらは、関数に渡された`HeapTupleHeader`型の引数、求められた属性の名前、属性がNULLであるかどうかを通知する返りパラメータです。 `GetAttributeByName`は適切な`DatumGet`XXX`()`マクロを使用して適切なデータ型に変換可能な`Datum`型の値を返します。 このNULLフラグが設定されている場合、戻り値の意味がないことに注意し、この結果で何かを行おうとする前に常に、NULLフラグを検査してください。

対象列を名前ではなく列番号で選択する`GetAttributeByNum`もあります。

下記のコマンドで`c_overpaid`関数をSQLで宣言します。

    CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
        AS 'DIRECTORY/funcs', 'c_overpaid'
        LANGUAGE C STRICT;

入力引数がNULLかどうかを検査する必要がないように`STRICT`を使用していることに注意してください。

## 35.9.8. 行（複合型）を返す

C言語関数から行もしくは複合型の値を返すために、複合型の複雑な作成のほとんどを隠蔽するマクロや関数を提供する、特別なAPIを使用することができます。 このAPIを使用するためには、ソースファイルで以下をインクルードする必要があります。

    #include "funcapi.h"

複合型のデータ値（以降"タプル"と記す）を作成する2つの方法があります。 Datum値の配列から作成する方法、もしくはタプルのある列の型の入力変換関数に渡すことができるC文字列の配列から作成することです。 どちらの方法でも、まずタプル構造体用の`TupleDesc`記述子を入手、あるいは作成しなければなりません。 Datumを使用する場合は、`TupleDesc`を`BlessTupleDesc`に渡し、各行に対して`heap_form_tuple`を呼び出します。 C文字列を使用する場合は、`TupleDesc` を`TupleDescGetAttInMetadata`に渡し、各行に対して `BuildTupleFromCStrings`を呼び出します。 タプルの集合を返す関数の場合、この設定段階を最初の関数呼び出しで一度にまとめて行うことができます。

必要な`TupleDesc`の設定用の補助用関数がいくつかあります。 ほとんどの複合型を返す関数での推奨方法は、以下の関数を呼び出し、呼び出し元の関数自身に渡される`fcinfo`構造体と同じものを渡すことです。

    TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo,
                                       Oid *resultTypeId,
                                       TupleDesc *resultTupleDesc)

（これにはもちろん、version 1呼び出し規約を使用していることが必要です。） `resultTypeId`を`NULL`とすることも、ローカル変数のアドレスを指定して関数の戻り値型のOIDを受け取ることができます。 `resultTupleDesc`はローカルな`TupleDesc`変数のアドレスでなければなりません。 結果が`TYPEFUNC_COMPOSITE`かどうかを確認してください。 `TYPEFUNC_COMPOSITE`であった場合、`resultTupleDesc`には必要な`TupleDesc`が格納されています。 （`TYPEFUNC_COMPOSITE`ではなかった場合、"レコード型を受け付けない文脈でレコードを返す関数が呼び出されました"というエラーを報告することができます。）

> ティップ: `get_call_result_type`は、多様性関数の結果の実際の型を解決することができます。 ですので、複合型を返す関数だけではなく、スカラの多様結果を返す関数でも有意です。 `resultTypeId`出力は主にスカラの多様結果を返す関数で有意です。

> 注意: `get_call_result_type`は、`get_expr_result_type`と似たような関数で、関数呼び出しで想定される出力型を式のツリー構造として解決します。 関数自身以外から結果型を決定したい場合に、これを使用することができます。 また、`get_func_result_type`という関数もあります。 これは関数のOIDが利用できる場合にのみ使用することができます。 しかし、これらの関数は、`record`型を返すものと宣言された関数では使用できません。 また、`get_func_result_type`は多様型を解決することができません。 したがって、優先して`get_call_result_type`を使用すべきです。

古く、廃止予定の`TupleDesc`を入手するための関数を以下に示します。

    TupleDesc RelationNameGetTupleDesc(const char *relname)

これを指名したリレーションの行型用の`TupleDesc`を取り出すために使用してください。 また、

    TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)

これを型のOIDに基づいて`TupleDesc`を取り出すために使用してください。 これは、基本型もしくは複合型の`TupleDesc`を取り出すために使用可能です。 これは`record`を返す関数ではうまく動作しません。 また、多様型を解決することもできません。

`TupleDesc`を獲得した後に、Datumを使用する場合は以下を呼び出してください。

    TupleDesc BlessTupleDesc(TupleDesc tupdesc)

C文字列を使用する場合は以下を呼び出してください。

    AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)

集合を返す関数を作成する場合は、これらの関数の結果を`FuncCallContext`構造体に格納してください。 それぞれ`tuple_desc`と`attinmeta`を使用します。

Datumを使用する場合は、ユーザデータをDatum形式に格納した`HeapTuple`を構築するために以下を使用します。

    HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull)

C文字列を使用する場合は、ユーザデータをC文字列形式に格納した`HeapTuple`を構築するために以下を使用します。

    HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)

`values`は行の各属性を1要素としたC文字列の配列です。 各C文字列は、属性のデータ型用の入力関数が受け付け可能な形式でなければなりません。 属性の値をNULL値として返すためには、`values`配列の対応するポインタに`NULL`を設定してください。 この関数は返す行それぞれに対して繰り返し呼び出す必要があります。

関数から返すタプルを構築し終わったら、それを`Datum`に変換しなければなりません。 以下を使用して、`HeapTuple`を有効なDatumに変換してください。

    HeapTupleGetDatum(HeapTuple tuple)

単一行のみを返すのであれば、この`Datum`を直接返すことができます。 さもなくば、集合を返す関数における現在の戻り値として使用することができます。

次節に例を示します。

## 35.9.9. 集合を返す

C言語関数から集合（複数行）を返す機能のために特殊なAPIが用意されています。 集合を返す関数は、Version 1呼び出し規約に従う必要があります。 また、ソースファイルは上述の通り`funcapi.h`をインクルードする必要があります。

集合を返す関数（SRF）は返される項目ごとに呼び出されます。 そのため、SRFは、過去の操作を記憶して呼び出しの度に次の項目を返すために十分な状態を保っている必要があります。 この処理を制御を補助するための`FuncCallContext`構造体が備わっています。 関数内では、複数の呼び出しにまたがる`FuncCallContext`へのポインタを保持するには、`fcinfo->flinfo->fn_extra`を使用します。

    typedef struct
    {
        /*
         * 既に行われた呼び出しの回数。
         *
         * SRF_FIRSTCALL_INIT()によってcall_cntrが0に初期化され、
         * SRF_RETURN_NEXT()が呼び出される度に増分されます。
         */
        uint32 call_cntr;

        /*
         * 省略可能 : 呼び出しの最大数
         *
         * max_callsは、便宜上用意されているだけで、設定は省略可能です。
         * 設定されていなければ、関数が終了したことを知るための別の方法を
         * 用意する必要があります。
         */
        uint32 max_calls;

        /*
         * 省略可能 : 結果スロットへのポインタ
         *
         * これは廃止され、後方互換性、すなわち非推奨のTupleDescGetSlot()を使用する
         * ユーザ定義のSRFのためにだけ存在します。
         */
        TupleTableSlot *slot;

        /*
         * 省略可能 : 様々なユーザによるコンテキスト情報へのポインタ
         *
         * user_fctxは、関数の呼び出し間の任意のコンテキスト情報
         * を取得するためのユーザ独自の構造へのポインタとして使用されます。
         */
        void *user_fctx;

        /*
         * 省略可能 : 属性型入力メタ情報を含んだ構造体へのポインタ
         *
         * attinmeta はタプル（つまり複合データ型）を返す際に使用され、
         * 基本データ型を返す場合には必要ありません。
         * BuildTupleFromCStrings()を使用して返されるタプルを作成する場合にのみ必要です。
         */
        AttInMetadata *attinmeta;

        /*
         *  複数の呼び出しで必要とされる構造体に使われるメモリコンテキスト
         *
         * multi_call_memory_ctxは、SRF_FIRSTCALL_INIT()によってに設定され、
         * SRF_RETURN_DONE()がクリーンアップの際に使用します。
         * これはSRFの複数呼び出しで再利用される全てのメモリ用に最も適切なメモリコンテキストです。
         */
        MemoryContext multi_call_memory_ctx;
        /*
         * 省略可能: タプル説明を含む構造体へのポインタ。
         * tuple_descはタプル（つまり複合データ型）を返す場合に使用され、BuildTupleFromCStrings()
         * ではなくheap_form_tuple()を使用してタプルを作成する場合にのみ必要です。
         * 通常ここに格納されるTupleDescは最初にBlessTupleDesc()を最初に実行したものでなければなり
         * ません。
         */
        TupleDesc tuple_desc;

    } FuncCallContext;

SRFはいくつかの関数およびマクロを使用して`FuncCallContext`構造体を自動的に操作します（また`fn_extra`で検索することを想定します)。

    SRF_IS_FIRSTCALL()

を使用して、その関数呼び出しが初回のものであるか、2回目以降であるかを判断します。 最初の呼び出し（のみ）で、

    SRF_FIRSTCALL_INIT()

を使用して、`FuncCallContext`を初期化します。 最初の呼び出しを含むすべての呼び出しで、

    SRF_PERCALL_SETUP()

を使用して、`FuncCallContext`を使用するための適切な設定を行い、以前の受け渡しから残っている結果データを消去します。

関数で返すべきデータがある場合は、

    SRF_RETURN_NEXT(funcctx, result)

を使用して、そのデータを呼び出し側に返します。 （先に説明した通り `result`は`Datum`型、つまり1つの値またはタプルである必要があります。） 最後に、関数がデータを返し終わったら、

    SRF_RETURN_DONE(funcctx)

を使用してSRFを片付け、終了します。

SRFの呼び出し時に現行になっているメモリコンテキストは一時的なコンテキストで、各呼び出しの間に消去されます。 つまり`palloc`を使用して割り当てたもののすべてを`pfree`する必要はありません。 これらはいずれ消去されるものだからです。 しかし、データ構造体を複数の呼び出しに渡って使用するように割り当てる場合は、どこか別の場所に置いておく必要があります。 `multi_call_memory_ctx`によって参照されるメモリコンテキストは、SRFの実行が終わるまで使用可能にしなければならないデータの保管場所として適しています。 つまり、ほとんどの場合、最初の呼び出しのセットアップ中に`multi_call_memory_ctx`へ切り替える必要があるということです。

完全な疑似コードの例を示します。

    Datum
    my_set_returning_function(PG_FUNCTION_ARGS)
    {
        FuncCallContext  *funcctx;
        Datum             result;
        further declarations as needed

        if (SRF_IS_FIRSTCALL())
        {
            MemoryContext oldcontext;

            funcctx = SRF_FIRSTCALL_INIT();
            oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
            /* 一度限りのセットアップコードがここに入ります: */
            user code
            if returning composite
                build TupleDesc, and perhaps AttInMetadata
            endif returning composite
            user code
            MemoryContextSwitchTo(oldcontext);
        }

        /* 毎回実行するセットアップコードがここに入ります: */
        user code
        funcctx = SRF_PERCALL_SETUP();
        user code

        /* これは、終了したかどうかをテストする方法の1つです: */
        if (funcctx->call_cntr < funcctx->max_calls)
        {
            /* ここで、別の項目を返します: */
            user code
            obtain result Datum
            SRF_RETURN_NEXT(funcctx, result);
        }
        else
        {
            /* これで項目を返し終わりました。 後はクリーンアップするだけです。 */
            user code
            SRF_RETURN_DONE(funcctx);
        }
    }

複合型を返す単純なSRFの完全な例は以下の通りです。

    PG_FUNCTION_INFO_V1(retcomposite);

    Datum
    retcomposite(PG_FUNCTION_ARGS)
    {
        FuncCallContext     *funcctx;
        int                  call_cntr;
        int                  max_calls;
        TupleDesc            tupdesc;
        AttInMetadata       *attinmeta;

         /* 関数の最初の呼び出し時にのみ実行 */
         if (SRF_IS_FIRSTCALL())
         {
            MemoryContext   oldcontext;

            /* 呼び出し間で永続化する関数コンテキストを作成 */
            funcctx = SRF_FIRSTCALL_INIT();

            /* 複数関数呼び出しに適切なメモリコンテキストへの切り替え */
            oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

            /* 返されるタプルの合計数 */
            funcctx->max_calls = PG_GETARG_UINT32(0);

            /*  結果型用のタプル記述子を作成 */
            if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("function returning record called in context "
                                "that cannot accept type record")));

            /*
             * 後で未加工のC文字列からタプルを作成するために必要となる
             * 属性メタデータの生成
             */
            attinmeta = TupleDescGetAttInMetadata(tupdesc);
            funcctx->attinmeta = attinmeta;

            MemoryContextSwitchTo(oldcontext);
        }

        /* 全ての関数呼び出しで実行 */
        funcctx = SRF_PERCALL_SETUP();

        call_cntr = funcctx->call_cntr;
        max_calls = funcctx->max_calls;
        attinmeta = funcctx->attinmeta;

        if (call_cntr < max_calls)    /* 他にも送るものがある場合  */
        {
            char       **values;
            HeapTuple    tuple;
            Datum        result;

            /*
             * 返すタプルを構築するためのvalues配列を用意します。
             * これは、後で適切な入力関数で処理される
             * C文字列の配列でなければなりません。
             */
            values = (char **) palloc(3 * sizeof(char *));
            values[0] = (char *) palloc(16 * sizeof(char));
            values[1] = (char *) palloc(16 * sizeof(char));
            values[2] = (char *) palloc(16 * sizeof(char));

            snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
            snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
            snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));

            /* タプルの作成 */
            tuple = BuildTupleFromCStrings(attinmeta, values);

            /* タプルをdatumに変換 */
            result = HeapTupleGetDatum(tuple);

            /* クリーンアップ（これは必須ではありません） */
            pfree(values[0]);
            pfree(values[1]);
            pfree(values[2]);
            pfree(values);

            SRF_RETURN_NEXT(funcctx, result);
        }
        else    /* 何も残っていない場合 */
        {
            SRF_RETURN_DONE(funcctx);
        }
    }

以下にこの関数をSQLで宣言する一例を示します。

    CREATE TYPE __retcomposite AS (f1 integer, f2 integer, f3 integer);

    CREATE OR REPLACE FUNCTION retcomposite(integer, integer)
        RETURNS SETOF __retcomposite
        AS 'filename', 'retcomposite'
        LANGUAGE C IMMUTABLE STRICT;

他にも以下のようにOUTパラメータを使用する方法もあります。

    CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
        OUT f1 integer, OUT f2 integer, OUT f3 integer)
        RETURNS SETOF record
        AS 'filename', 'retcomposite'
        LANGUAGE C IMMUTABLE STRICT;

この方法では、関数の出力型は形式上無名の`record`型になることに注意してください。

ソース配布物内のcontrib/tablefuncモジュールのディレクトリには、集合を返す関数のより多くの例があります。

## 35.9.10. 引数と戻り値の多様性

C言語関数は、`anyelement`、`anyarray`、`anynonarray`および`anyenum`多様型を受け付ける、または返すように宣言することができます。 多様関数の詳細な説明は項35.2.5を参照してください。 関数の引数もしくは戻り値が多様型として定義される時、関数の作成者は前もって呼び出しにおけるデータ型や返すべきデータ型が何であるかを知ることはできません。 Version-1 C関数で引数の実データ型と、返すべきと想定された型を発見できるための2つのルーチンが`fmgr.h`に用意されています。 このルーチンは`get_fn_expr_rettype(FmgrInfo *flinfo)`と`get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)`という名前です。 これらは結果もしくは引数型のOIDを返します。 ただし、もし情報が利用できなければ`InvalidOid`を返します。 `flinfo`構造体は通常`fcinfo->flinfo`としてアクセスされます。 `argnum`パラメータは0から始まります。 また、`get_fn_expr_rettype`の代わりに`get_call_result_type`を使用することもできます。

例えば、任意の型の単一要素を受け付け、その型の1次元配列を返す関数を考えてみます。

    PG_FUNCTION_INFO_V1(make_array);
    Datum
    make_array(PG_FUNCTION_ARGS)
    {
        ArrayType  *result;
        Oid         element_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
        Datum       element;
        bool        isnull;
        int16       typlen;
        bool        typbyval;
        char        typalign;
        int         ndims;
        int         dims[MAXDIM];
        int         lbs[MAXDIM];

        if (!OidIsValid(element_type))
            elog(ERROR, "could not determine data type of input");

        /* 与えられた要素がNULLかどうか注意しつつ、要素を取り出します。*/
        isnull = PG_ARGISNULL(0);
        if (isnull)
            element = (Datum) 0;
        else
            element = PG_GETARG_DATUM(0);

        /* 次元数は1 */
        ndims = 1;
        /* 要素を1つ */
        dims[0] = 1;
        /* 下限は1 */
        lbs[0] = 1;

        /* この要素型に関する必要情報を取り出す。 */
        get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

        /* ここで配列を作成 */
        result = construct_md_array(&element, &isnull, ndims, dims, lbs,
                                    element_type, typlen, typbyval, typalign);

        PG_RETURN_ARRAYTYPE_P(result);
    }

以下のコマンドはSQLで`make_array`関数を宣言します。

    CREATE FUNCTION make_array(anyelement) RETURNS anyarray
        AS 'DIRECTORY/funcs', 'make_array'
        LANGUAGE C IMMUTABLE;

C言語関数でのみ使用できる多様性の変異体があります。 `"any"`型のパラメータを取るように宣言できます。 （この型名は、SQL予約語でもあるため二重引用符で括らなくてはならないことに注意してください。） これは、他の`"any"`引数が同じ型になることを強要することも、関数の結果型の決定を支援することもない点を除いて、`anyelement`のように動作します。 C言語関数は最終パラメータが`VARIADIC "any"`であるように宣言可能です。 これは任意の型の１つ以上の実引数と一致します（同じ型である必要はありません）。 これらの引数は、通常のvariadic関数で起こったように、配列の中にまとめられません。 それらは単に別々に関数に渡されるだけです。 `PG_NARGS()`マクロと上に記載したメソッドは、この機能を使用するときに実際の引数とその型を決定するため使用されなければなりません。

## 35.9.11. 共有メモリとLWLocks

アドインはLWLocks（軽量ロック）とサーバ起動時に共有メモリの割り当てを保持することができます。 shared_preload_librariesで指定して、こうしたアドインの共有ライブラリを事前にロードしなければなりません。 共有メモリは、その`_PG_init`関数で以下を呼び出すことで保持されます。

    void RequestAddinShmemSpace(int size)

LWLocksはその`_PG_init`関数で以下を呼び出すことで保持されます。

    void RequestAddinLWLocks(int n)

競合状態の可能性を防止するために、割り当てられた共有メモリへの接続やその初期化時に、以下のように各バックエンドで`AddinShmemInitLock`軽量ロックを使用しなければなりません。

    static mystruct *ptr = NULL;

    if (!ptr)
    {
            bool    found;

            LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
            ptr = ShmemInitStruct("my struct name", size, &found);
            if (!found)
            {
                    initialize contents of shmem area;
                    acquire any requested LWLocks using:
                    ptr->mylockid = LWLockAssign();
            }
            LWLockRelease(AddinShmemInitLock);
    }

## 35.9.12. 拡張へのC++の利用

以下のガイドラインに従うことで、PostgreSQLの拡張を構築するためC++モードのコンパイラを利用できます。

- バックエンドからアクセスされる関数はすべてバックエンドに対してCインタフェースを提供しなければなりません。 このC関数はC++関数を呼びだすことができます。 例えば、バックエンドからアクセスされる関数には`extern C`リンクが必要です。 これはバックエンドとC++コードの間でポインタとして渡される関数にも必要です。

- 適切な解放メソッドを使ってメモリを解放してください。 例えば、ほとんどのバックエンドメモリは`palloc()`で確保されますので、`pfree()`を使って解放してください。 この場合にC++の`delete()`を使うと失敗するでしょう。

- 例外がCコードへ伝播しないようにしてください（`extern C`関数すべての最上位ですべての例外を捕捉するブロックを使ってください）。 メモリ不足のようなイベントにより例外が発生する可能性がありますので、C++コードが何も例外を発生させない場合であっても、これは必要です。 例外はすべて捕捉しなければなりません。 そして適切なエラーをCインタフェースに渡してください。 可能であれば、例外を完全に除去できるように`-fno-exceptions`を付けてC++をコンパイルしてください。 その場合、例えば`new()`で返されるNULLの検査など、C++コード内で失敗の検査を行わなければなりません。

- C++コードからバックエンド関数を呼び出す場合には、C++呼び出しスタック内にC言語互換構造体（POD）のみが含まれていることを確認してください。 バックエンドのエラーは、非PODオブジェクトを持つC++呼び出しスタックを適切に戻すことができない、長距離`longjmp()`を生成しますので、これは必要です。

まとめると、バックエンドとやりとりするための壁の役割を担う`extern C`関数の背後にC++コードを配置して、例外、メモリ、呼び出しスタックそれぞれの漏れを避けるのが最善です。

前のページ | ホーム | 次のページ
内部関数 | 上に戻る | ユーザ定義の集約