13.4. コンパイルされたC言語関数

C言語で書かれた関数は動的にロード可能オブジェクト(シェアードライブラリ)に コンパイルすることができ、ユーザ定義されたSQL関数の実効の際に 使用されます。バックエンドセッションで、特定のロード可能 オブジェクトファイルで初めてユーザ定義関数が呼ばれた時、動的ローダは、 関数を呼び出し可能とするために、そのオブジェクトファイルをメモリにロードします。 したがって、ユーザ定義関数のためのCREATE FUNCTIONでは 関数に関する2つの情報、ロード可能オブジェクトファイル名と そのオブジェクトファイル内で呼び出すための、特定の関数のC言語名(リンクシンボル)、 を明記する必要があります。C言語名が明記されていない場合は、 SQL関数名と同じ関数名として認識されます。

Note: その関数が一度使用されたら、動的にロードされたユーザ関数はメモリに 記憶され、その関数が後に同じセッション内で呼ばれた時には シンボルテーブルを参照するための、極わずかなオーバーヘッドが かかるだけとなります。

オブジェクトファイルを指定する文字列(AS句の最初の文字列)は その関数のオブジェクトコードファイルの絶対パス であり、シングルクォーテーションで囲われている必要があります。 AS句でリンクシンボルが与えられている場合は、そのリンクシンボルも シングルクォーテーションで囲われている必要があり、 Cのソースコード内に記述されている関数名と全く等しくなければなりません。 Unixシステムのnmコマンドは、動的にロード可能な オブジェクトのすべてのリンクシンボルを表示させます。

Note: Postgresでは、自動的に関数を コンパイルしません。CREATE FUNCTIONコマンドで使用される前に コンパイルされる必要があります。詳細は下記をご覧下さい。

C言語関数では、現在2つの呼び出し規約が使用されています。 新しい"version 1"呼び出し規約は、下記のように、関数の PG_FUNCTION_INFO_V1()マクロ呼び出しを記述して 示されています。このようなマクロの欠如は古いスタイル("version 0")関数を 示します。CREATE FUNCTIONで指定された言語名は、どちらの場合ともC言語です。 古いスタイルの関数は、移植性の問題と機能の不足のために、 互換性の理由のためのみに現在存在しています。

13.4.1. C言語関数の基本型

下記のテーブルはPostgresにロードされるC言語関数の引数で 必要とされるC言語の型です。"Defined In"列では、同等のC言語の型が 定義されている実際のヘッダーファイル (.../src/backend/ディレクトリ)を示しています。 必ず始めにpostgres.hを含め、それが c.hを含めることにご注意下さい。

Table 13-1. Postgresの基本型(組み込まれている型)と同等なC言語型

組み込まれている型 C言語型 定義場所
abstimeAbsoluteTimeutils/nabstime.h
boolboolinclude/c.h
box(BOX *)utils/geo-decls.h
bytea(bytea *)include/postgres.h
"char"charN/A
cidCIDinclude/postgres.h
datetime(DateTime *)include/c.h or include/postgres.h
int2int2 or int16include/postgres.h
int2vector(int2vector *)include/postgres.h
int4int4 or int32include/postgres.h
float4(float4 *)include/c.h or include/postgres.h
float8(float8 *)include/c.h or include/postgres.h
lseg(LSEG *)include/geo-decls.h
name(Name)include/postgres.h
oidoidinclude/postgres.h
oidvector(oidvector *)include/postgres.h
path(PATH *)utils/geo-decls.h
point(POINT *)utils/geo-decls.h
regprocregproc or REGPROCinclude/postgres.h
reltimeRelativeTimeutils/nabstime.h
text(text *)include/postgres.h
tidItemPointerstorage/itemptr.h
timespan(TimeSpan *)include/c.h or include/postgres.h
tintervalTimeIntervalutils/nabstime.h
xid(XID *)include/postgres.h

Postgresの内部では、基本型を "blob of memory(メモリの斑点)"と見なしています。 ある型について定義したユーザ定義関数は、同様に Postgresがその型をどのように 実装するかを定義しています。つまり、Postgresは データをディスクに保存し、ディスクからデータを受取り、 ユーザ定義関数でそのデータを入力値として受け取り、 処理/出力するために使用します。基本型は下記の3つのいずれかの 内部フォーマットを使用しています。

値として渡す型は1、2、又は4バイト長のみ可能です。 (使用するマシンのsizeof(Datum)が8の場合は8バイトも可能です。) データ型を定義する際、その型が全てのアーキテクチャにおいて 同一の大きさ(バイト数)となるように定義することに注意して下さい。 例えば、long型はマシンによっては4バイトであったり、 8バイトであったりして危険ですが、int型はほとんどの Unixマシンでは4バイトです(しかし、多くのパーソナルコンピュータでは異ります)。 Unixマシンの一般的なint4型の実装は 下記のようなものとなります。

/* 4-byte integer, passed by value */
typedef int int4;

一方、任意の大きさの固定長の型は参照として引き渡すことが 可能です。下記の、Postgresの型の 実装サンプルをご覧下さい。

/* 16-byte structure, passed by reference */
typedef struct
{
    double  x, y;
} Point;

それらの型のポインタのみがPostgres関数の 入出力時に使用できます。それらの型を返すためには、palloc() を使用して正しい大きさのメモリ領域を割り当て、そのメモリ領域に 値を入力し、それのポインタを返します。(あるいは、ポインタを 返すことによって同じ型の入力値を返すことも可能です。しかし、 参照として渡すものの入力値の内容を決して変更しないで下さい。)

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

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

ここで示されているデータフィールドは、明かにすべての取り得る 文字列を保持できる長さではありません。C言語では このような構造を定義することは不可能です。可変長型を操作する場合、 正しいメモリ領域を割り当て、lengthフィールドを初期化しなければ なりません。例えば、text構造に40バイト保持したい場合は、 下記のようなコードを実行します。

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

これで基本型用の全てのあり得る構造体についての説明が 完了しましたので、実 際の関数の例を幾つか示します。

13.4.2. C言語関数のためのVersion-0の呼び出し規約

まず最初に使用できませんが、理解しやすいので、 "古いスタイル(old style)"の呼び出し規約を記述します。 version-0メソッドでは、C言語関数の引数を結果は、通常のCのプログラムの 記述の方法と同じような形式で行いますが、上記の説明のように、 各SQLのデータ型を示すC言語を使用する際にはご注意下さい。

ここに例を記します。

#include "postgres.h"
#include <string.h>

/* By Value */
         
int
add_one(int arg)
{
    return arg + 1;
}

/* By Reference, Fixed Length */

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;
}

/* By Reference, Variable Length */

text *
copytext(text *t)
{
    /*
     * VARSIZE is the total size of the struct in bytes.
     */
    text *new_t = (text *) palloc(VARSIZE(t));
    VARATT_SIZEP(new_t) = VARSIZE(t);
    /*
     * VARDATA is a pointer to the data region of the struct.
     */
    memcpy((void *) VARDATA(new_t), /* destination */
           (void *) VARDATA(t),     /* source */
           VARSIZE(t)-VARHDRSZ);    /* how many bytes */
    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);

    VARATT_SIZEP(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というファイルに 予め用意され、シェアードオブジェクトにコンパイルされていたとし、 Postgresには下記のコマンドを 使って定義できます。

CREATE FUNCTION add_one(int4) RETURNS int4
     AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
     WITH (isStrict);

-- note overloading of SQL function name add_one()
CREATE FUNCTION add_one(float8) RETURNS float8
     AS 'PGROOT/tutorial/funcs.so',
        'add_one_float8'
     LANGUAGE 'c' WITH (isStrict);

CREATE FUNCTION makepoint(point, point) RETURNS point
     AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
     WITH (isStrict);
                         
CREATE FUNCTION copytext(text) RETURNS text
     AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
     WITH (isStrict);

CREATE FUNCTION concat_text(text, text) RETURNS text
     AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
     WITH (isStrict);

ここでPGROOTPostgresの ソースツリーの絶対パスを意味します。システムによっては、 シェアードオブジェクトのファイル名の拡張子が.so ではなく、.slの場合や、それ以外の可能性も あるので、システムによって変更しなければならないことにご注意下さい。

ここで、関数を"精密"と特定していることに注目して下さい。 それは、もし入力された値がNULLであった場合に、システムが自動的に 返り値がNULLであると推定しないことを意味します。これを 行うことによって、関数のコードで入力値がNULLであるかどうかの チェックを行う必要がありません。これがなければ、各参照渡しの要素に 対してそれがNULLのポインタであるかどうかのチェックを 行うなどの、NULLの明示的なチェックを行う必要性が出てきます。 (値渡しに関しては、チェックを行う方法は存在しません。)

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

13.4.3. C言語関数のためのVersion-1の呼び出し規約

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

Datum funcname(PG_FUNCTION_ARGS)
また、下記のマクロの呼び出し
PG_FUNCTION_INFO_V1(funcname);
が必ず同じソースファイルに書かれている必要があります (規約では、関数の直前で書かれています)。現在Postgresでは すべての内部関数はversion-1であると認識するので、 このマクロの呼び出しは内部言語関数では必要ありません。 しかし、動的にロードされる関数では必要です

version-1関数では、それぞれの実際の引数は、引数の型に合った PG_GETARG_xxx()マクロ を使用して呼び出され、結果は返り型に合った PG_RETURN_xxx()マクロ を使用して返されます。

下記は上記と同じ関数をversion-1形式で記述したものです。

#include "postgres.h"
#include <string.h>
#include "fmgr.h"

/* By Value */

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

    PG_RETURN_INT32(arg + 1);
}

/* By Reference, Fixed Length */

PG_FUNCTION_INFO_V1(add_one_float8);

Datum
add_one_float8(PG_FUNCTION_ARGS)
{
    /* The macros for FLOAT8 hide its pass-by-reference nature */
    float8   arg = PG_GETARG_FLOAT8(0);

    PG_RETURN_FLOAT8(arg + 1.0);
}

PG_FUNCTION_INFO_V1(makepoint);

Datum
makepoint(PG_FUNCTION_ARGS)
{
    /* Here, the pass-by-reference nature of Point is not hidden */
    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);
}

/* By Reference, Variable Length */

PG_FUNCTION_INFO_V1(copytext);

Datum
copytext(PG_FUNCTION_ARGS)
{
    text     *t = PG_GETARG_TEXT_P(0);
    /*
     * VARSIZE is the total size of the struct in bytes.
     */
    text     *new_t = (text *) palloc(VARSIZE(t));
    VARATT_SIZEP(new_t) = VARSIZE(t);
    /*
     * VARDATA is a pointer to the data region of the struct.
     */
    memcpy((void *) VARDATA(new_t), /* destination */
           (void *) VARDATA(t),     /* source */
           VARSIZE(t)-VARHDRSZ);    /* how many bytes */
    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);

    VARATT_SIZEP(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マクロは"toasted"値(圧縮、または行外)を呼び出す詳細を伏せます。 上記にある、古いスタイルのcopytext関数と concat_text関数は、toasted値としては、 入力にpg_detoast_datum()を使用しないので、 実際は誤ったものとなります。(古いスタイルの動的にロードされる関数 のハンドラーは、現在、この問題に対処していますが、 version-1関数で実行可能なことよりも劣ります。)

version-1関数の1つの大きな改善点は、NULLの入力/結果の 処理能力です。PG_ARGISNULL(n)マクロ関数は 各入力がNULLであるかどうかのテストを可能としています。 (これは、"精密に"関数が定義されていない場合のみ必要です。) PG_GETARG_xxx()マクロでは、 入力された引数は始まり値をゼロとし、数え上げられます。結果としてNULLを 返す場合は、PG_RETURN_NULL()を実行します。 これは、精密/精密でない関数の両方で使用可能です。

version-1関数呼び出し規約では、"セット(set)"の 結果を返すことが可能で、トリガ関数と手続型言語の呼び出しハンドラを 実行しています。また、Version-1コードはANSI C制約が関数呼び出しプロトコルで 守られているので、version-0よりも移植性があります。 詳細はソースディストリビューションの src/backend/utils/fmgr/READMEをご覧下さい。

13.4.4. 複合型を使用したC言語関数

複合型では、C言語のような固定のレイアウトがありません。 複合型のインスタンスはNULLフィールドを持つ可能性があります。 また、複合型で、継承階層の一部であるものは同じ継承の階層の 他のメンバとは異なるフィールドを持つ可能性もあります。 そのため、Postgresは C言語から複合型のフィールドにアクセスするための手続きの インタフェースを提供します。Postgres が行の集合を処理するにつれ、各行は不明瞭なTUPLE 型の構造体として引き渡されます。下記の問い合わせを答える 関数を書こうとしていると仮定します。

SELECT name, c_overpaid(emp, 1500) AS overpaid
FROM emp
WHERE name = 'Bill' OR name = 'Sam';
上記の問い合わせでは、 c_overpaidを下記のように定義することが できます。
#include "postgres.h"
#include "executor/executor.h"  /* for GetAttributeByName() */

bool
c_overpaid(TupleTableSlot *t, /* the current row of EMP */
           int32 limit)
{
    bool isnull;
    int32 salary;

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

/* In version-1 coding, the above would look like this: */

PG_FUNCTION_INFO_V1(c_overpaid);

Datum
c_overpaid(PG_FUNCTION_ARGS)
{
    TupleTableSlot  *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
    int32            limit = PG_GETARG_INT32(1);
    bool isnull;
    int32 salary;

    salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
    if (isnull)
        PG_RETURN_BOOL(false);
    /* Alternatively, we might prefer to do PG_RETURN_NULL() for null salary */

    PG_RETURN_BOOL(salary > limit);
}

GetAttributeByNameは、現在の行から属性を返す、 Postgresシステム関数です。これには3つの 引数があります。それらはTupleTableSlot*型が関数に 引き渡された時の引数、求められた属性の名前、属性がNULLであるかどうかの 返りパラメーターです。GetAttributeByNameは 適切なDatumGetXXX() マクロを使用して適切なデータ型に転換させれるデータの値を返します。

下記の問い合わせはPostgresc_overpaid関数を認識させるものです。

CREATE FUNCTION c_overpaid(emp, int4) 
RETURNS bool
AS 'PGROOT/tutorial/obj/funcs.so' 
LANGUAGE 'c';

C言語を用いて、新しい行を作成したり、現存する行を変更させたりする 方法がありますが、このマニュアルで述べるには難易度がとても 高いので、省略します。

13.4.5. コードを書く

ここからは、より難易度の高い、プログラミング言語関数の 説明となります。注意して頂きたいのは、このセクションの主旨は、 プログラマを養成することではありません。 PostgresC関数を 書く前に、C言語についてよく理解している (ポインタやmallocメモリ管理も含めて)必要があります。 C言語以外の言語で記述した関数を Postgresに組み込むことは可能ですが、 例えばFORTRANPascalといった 言語はC言語と同じ呼び出し規定 ではないので、多くの場合、困難です。それはつまり、 他の言語では同じ方法で引数を渡したり結果を返すことを行わないという ことです。このため、プログラミング言語関数はC 言語で書かれているものと仮定します。

C関数を作成する時の基本的な規則は下記のものです。

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

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

詳しい情報は、オペレーティングシステムのドキュメント、特にCコンパイラ cc、リンクエディタ ld、を読んで ください。 更にPostgreSQLのソースコードに contribディレクトリにいくつか実例があります。しかし、 もしこれらの例に頼るとPostgreSQLソースコード の有効性に依存したモジュールが作られてしまいます。

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

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

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

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

Digital Unix/Tru64 UNIX

PICがデフォルトですから、コンパイルのコマンド は通常のままでよいです。リンクするためには特別なオプションがついた ldが使われます。

cc -c foo.c
ld -shared -expect_unresolved '*' -o foo.so foo.o
システムコンパイラの代わりに、GCC と一緒に同じプロシージャが使われます。 特別なオプションは要求されません。

Solaris

PIC を作るためのコンパイラフラグは Sun コンパイラでは-KPICで、 GCCでは-fpic です。共有ライブラリをリンクするためには、どちらのコンパイラ でも-Gで、GCC では代わりに-sharedです。

cc -KPIC -c foo.c
cc -G -o foo.so foo.o
or
gcc -fpic -c foo.c
gcc -G -o foo.so foo.o

Unixware

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

cc -K PIC -c foo.c
cc -G -o foo.so foo.o
or
gcc -fpic -c foo.c
gcc -shared -o foo.so foo.o

Tip: もし作った拡張モジュールをより広く配布するためのパッケージ にしたい場合、GNU Libtoolを共有ライブラリの構築に使う ことを見当するべきです。これはプラットフォームの違いを 一般的で力強いインタフェースにカプセル化します。 真剣なパッケージ作成はさらにライブラリバージョン、シンボル 分解メソッド、その他の検討も必要になります。

その結果できた共有メモリファイルはPostgres にロードすることができます。CREATE FUNCTIONコマンド にファイル名を指定する時は、単純なオブジェクトファイルではなく 共有ライブラリ名の名前(.soで終るもの)を 渡して下さい。

Note: 実際はPostgresはそのファイルが 共有ライブラリファイルである限り名前が何であっても構いません。

CREATE FUNCTIONコマンドに与えられるパスは Postgresサーバが動いているマシン上で 見えるディレクトリを参照する絶対パス(つまり/で 始まります)でなければいけません。相対パスでも実は動きますが、 これはデータベースがあるディレクトリにたいして相対なのです (それは一般的にはフロントエンドアプリケーションからは見えません)。 明かに、ユーザがフロントエンドアプリケーションを始めた ディレクトリに相対するパスを作っても意味がありません。なぜなら サーバは全く別のマシンで動いているかもしれないのです! Postgresサーバが動く際、 CREATE FUNCTIONコマンドで指定されるパスを 通り、共有ライブラリファイルを読めなくてはいけません。 ("postgres"ユーザが読めない、あるいは実行できない ファイルもしくはより上のレベルのディレクトリを作ることは よくある間違いです。