#author("2017-06-03T13:40:28+09:00","default:haikikyou","haikikyou")
#author("2018-02-20T14:47:02+00:00","default:haikikyou","haikikyou")
[[PostgreSQL/開発]]

#contents

* 概要 [#t03f8105]

- 概要説明
- 関数マネージャと関数呼び出しインターフェースの定義ファイル

#ref(./fmgr_overview.png,70%)
* 定義 [#m6ff2469]

** 変数,エイリアス [#r6993c38]

** マクロ [#n3bffc31]

** 列挙型 [#jc93a4e3]

** 構造体 [#e4aded44]
** 構造体 [#cb54ce43]

*** Struct [#ce92b362]
*** FmgrInfo [#dbdc9d3a]

|~番号|~データ型|~フィールド|~説明|h
|1|PGFunction|fn_addr|実行される関数のポインタ|
|2|Oid|fn_oid|関数のOID|
|3|short|fn_nargs|関数の引数の数|
|4|bool|fn_strict|関数がstrictモードが否か。&br;true:strictモード,false:strictモードでない|
|5|bool|fn_retset|setを返す関数か否か。&br;true:setを返す,false:setを返さない|
|6|unsigned char|fn_stats|関数が統計情報を収集するか。この値より大きな値の場合に収集する。&br;&size(12){&color(white,orange){ 参考 };}; [[enum TrackFunctionsLevel>https://doxygen.postgresql.org/pgstat_8h.html#a8674d83a2e7ca33c720348e4b328ab4c]] - on doxygen.postgresql.org|
|7|void*|fn_extra|関数で利用可能な領域|
|8|MemoryContext|fn_mcxt|fn_extraを格納するためのメモリコンテキスト|
|9|fmNodePtr|fn_expr|パースツリーのノード表現|

システムカタログの関数情報を保持する。

&size(12){&color(white,#00afcc){ サンプル };};
#geshi(c){{{
// 指定されたOIDの関数情報を取得する。
FmgrInfo *fmgr = palloc(sizeof(FmgrInfo));
fmgr_info((Oid)1396, fmgr); // abs(int64)の情報を構造体に格納する
}}}
*** FunctionCallInfoData [#vd0ca3c7]

|~番号|~データ型|~フィールド|~説明|h
|1|FmgrInfo*|flinfo|関数情報を保持する構造体のポインタ|
|2|fmNodePtr|context|関数呼び出しのコンテキスト。&br;例:TriggerData,AggState,WindowAggState|
|3|fmNodePtr|resultinfo|実行結果に関する情報。&br;例:ReturnSetInfo|
|4|Oid|fncollation|collation|
|5|bool|isnull|結果がNULLを返すか否か。&br;true:返す,false:返さない|
|6|short|nargs|関数の引数の数|
|7|Datum|arg[FUNC_MAX_ARGS]|引数|
|8|bool|argnull[FUNC_MAX_ARGS]|arg[i]がNULLであるか否か&br;true:NULL,false:NULLでない|

関数呼び出しに関する情報を保持する構造体。

&size(12){&color(white,orange){ 参考 };}; [[FunctionCallInfoData>https://doxygen.postgresql.org/structFunctionCallInfoData.html]] - on doxygen.postgresql.org

&size(12){&color(white,#00afcc){ サンプル };};
#geshi(c){{{
void test_invoke()
{
    Datum result;
    FunctionCallInfoData fcinfo;

    FmgrInfo *fmgr = palloc(sizeof(FmgrInfo));
    fmgr_info((Oid)1396, fmgr); // abs(int64);

    // Basic way
    int64 arg0 = -100;

    InitFunctionCallInfoData(fcinfo, fmgr, 1, 0, NULL, NULL);
    fcinfo.isnull = false;
    fcinfo.argnull[0] = false;
    fcinfo.arg[0] = Int64GetDatum(arg0);

    result = FunctionCallInvoke(&fcinfo);
    elog(INFO, "abs(-100) = %ld", DatumGetInt64(result));
}

/* Output is the following:
------------------------------
INFO:  abs(-100) = 100
*/
}}}


*** Pg_finfo_record [#jb9501fe]

|~番号|~データ型|~フィールド|~説明|h
|1|int|api_version|apiのバージョン|

関数インターフェースのバージョンを示す情報を格納する。独自の拡張機能や関数を作成するときに,以下のサンプルのようにマクロを使った際に展開され定義される。

&size(12){&color(white,#00afcc){ サンプル };};
#geshi(c){{{
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

PG_FUNCTION_INFO_V1(test_fdw_handler);
/* これは以下のように展開される
extern Datum test_fdw_handler(FunctionCallInfo fcinfo);
extern const Pg_finfo_record * pg_finfo_test_fdw_handler(void);
const Pg_finfo_record * pg_finfo_test_fdw_handler(void)
{
	static const Pg_finfo_record my_finfo = { 1 };
	return &my_finfo;
}
extern int no_such_variable;
/*
}}}
** 関数 [#ydff81f8]

*** 関数名 [#r22bc55b]
*** fmgr_info [#v3a0be5f]

#geshi(c){{
#geshi(c){{{
// 引数1:関数のOid
// 引数2:FmgrInfo構造体のポインタ
extern void fmgr_info(Oid functionId, FmgrInfo *finfo);
}}}

- 指定されたfunctionIdの関数情報を探索し,finfoに設定する。

&size(12){&color(white,orange){ 参考 };}; [[fmgr_info()>https://doxygen.postgresql.org/fmgr_8c.html#a4aaf89400b4741012e4ee1b715dae0e4]] - on doxygen.postgresql.org

&size(12){&color(white,#00afcc){ サンプル };};
#geshi(c){{{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo); // fill flinfo
}}}
*** fmgr_info_cxt [#pb3ac2bc]

#geshi(c){{{
// 引数1:関数のOid
// 引数2:FmgrInfo構造体のポインタ
// 引数3:fx_extraを使用する際のメモリコンテキスト
extern void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo,  MemoryContext mcxt);
}}}

- 指定されたfunctionIdの関数情報を探索し,finfoに設定する。
- fn_extraで使用されるスペースを指定されたメモリコンテキストから割り当てるよう設定する。


*** fmgr_info_copy [#m6566276]

#geshi(c){{{
// 引数1:コピー先のFmgrInfo構造体のポインタ
// 引数2:コピー元のFmgrInfo構造体のポインタ
// 引数3:fx_extraを使用する際のメモリコンテキスト
extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
}}}

- FmgrInfo構造体のデータをコピーする(memcpy)

&size(12){&color(white,#00afcc){ サンプル };};
#geshi(c){{{
FmgrInfo destinfo, srcinfo;
fmgr_info(1396, &srcinfo);
fmgr_info_copy(&destinfo, &srcinfo, CurrentMemoryContext);
}}}


*** DirectFunctionCall<N>Coll [#g95ad19a]

#geshi(c){{{
// 引数1:呼び出す関数のポインタ
// 引数2:collation
// 引数3:関数の引数
extern Datum DirectFunctionCall<N>Coll(PGFunction func, Oid collation, Datum arg1[, ...arg9]);
}}}

- fmgrを経由せず直接関数を呼び出す。

&size(12){&color(white,#00afcc){ サンプル };};
#geshi(c){{{
#include "postgres.h"
#include "fmgr.h"
#include "catalog/pg_collation.h"
#include "utils/builtins.h"

static void direct_function_sample(Datum arg1)
{
    Datum result;
    result = DirectFunctionCall1Coll(int4abs, DEFAULT_COLLATION_OID, arg1);
    elog(INFO, "result = %d", DatumGetInt32(result));
}
}}}


*** FunctionCall<N>Coll [#f221d4c3]

#geshi(c){{{
// 引数1:FmgrInfo構造体のポインタ
// 引数2:collation
// 引数3:関数の引数
extern Datum FunctionCall<N>Coll(FmgrInfo *flinfo, Oid collation, Datum arg1[, ...arg9]);
}}}

- 既に解決済みの関数情報を使って関数を呼び出す。

&size(12){&color(white,#00afcc){ サンプル };};
#geshi(c){{{
#include "postgres.h"
#include "fmgr.h"
#include "catalog/pg_collation.h"
#include "utils/builtins.h"

static void t_looked_up_function(Datum arg1)
{
    FmgrInfo fminfo;
    Oid pronOid;
    Datum result;
    
    pronOid = fmgr_internal_function("int4abs");
    fmgr_info(pronOid, &fminfo);
    
    result = FunctionCall1Coll(&fminfo, DEFAULT_COLLATION_OID, arg1);
    elog(INFO, "result = %d", DatumGetInt32(result));
}
}}}


***CallerFInfoFunction<N>Call [#j9befaee]

#geshi(c){{{
// 引数1:呼び出す関数のポインタ
// 引数2:FmgrInfo構造体のポインタ
// 引数2:collation
// 引数3〜:関数の引数
extern Datum CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1);
extern Datum CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2);
}}}

- 関数の呼び出し先でfn_extra、fn_mcxtフィールドを使用する場合に使う。

&size(12){&color(white,#00afcc){ サンプル };}; 
#geshi(c){{{
#include "postgres.h"
#include "fmgr.h"
#include "catalog/pg_collation.h"
#include "utils/builtins.h"

PG_FUNCTION_INFO_V1(my_txt_func);
Datum test_fn_extra_hash_function(PG_FUNCTION_ARGS);

Datum test_fn_extra_hash_function(PG_FUNCTION_ARGS)
{
    text *txt = PG_GETARG_TEXT_PP(0);
    MyCache *cache;
    cache = (MyCache *)(fcinfo->flinfo->fn_extra);
    char *cstring = text_to_cstring(txt);
    if (cache == NULL || strcmp(cache->str, cstring) != 0) {
        elog(INFO, "cache is null: %s", cstring);
        fcinfo->flinfo->fn_extra = (MyCache *)MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(MyCache));
        cache = (MyCache *)(fcinfo->flinfo->fn_extra);
        cache->str = cstring;
        cache->data = DirectFunctionCall1Coll(md5_text, InvalidOid, CStringGetTextDatum(cstring));
    } else {
        elog(INFO, "cache used: %s", cstring);
    }
    return cache->data;
}

Datum
my_txt_func(PG_FUNCTION_ARGS)
{
    Datum result, result2;
    result = CallerFInfoFunctionCall1(test_fn_extra_hash_function,
                                      fcinfo->flinfo,
                                      InvalidOid,
                                      PG_GETARG_DATUM(0));
    // fn_extraが使われているのを確認するため、もう一度呼び出してみる
    result2 = CallerFInfoFunctionCall1(test_fn_extra_hash_function,
                                      fcinfo->flinfo,
                                      InvalidOid,
                                      PG_GETARG_DATUM(0));
    PG_RETURN_TEXT_P(result2);
}
}}}



*** InputFunctionCall [#ucfe07c4]

#geshi(c){{{
// 引数1:入力変換関数の関数情報
// 引数2:引数文字列
// 引数3:引数の型を示すOid
// 引数4:ドメイン(ドメインを使用していない場合は、-1を指定すれば良い)
// 戻り値:入力変換関数の実行結果
extern Datum InputFunctionCall(FmgrInfo *flinfo, char *str,  Oid typioparam, int32 typmod);
}}}

- 指定された引数文字列を、入力変換関数を使って指定した型に変換する。

&label(sample){サンプル};
#geshi(c){{{
/*
 * select myfunc('100') + 1 => 101
 */
Datum
myfunc(PG_FUNCTION_ARGS)
{
	FmgrInfo fminfo;
	Oid pronOid,
		typinput,
		typioparam;
	Datum result;
	int32 typmod = -1;

	pronOid = fmgr_internal_function("int4in");
	fmgr_info(pronOid, &fminfo);
	if (pronOid == InvalidOid) {
		elog(ERROR, "not found int4in function");
	}

	text *txt = PG_GETARG_TEXT_PP(0);
	char *str = text_to_cstring(txt);

	/* Get input function and parameter information of type INT4 */
	getTypeInputInfo(INT4OID, &typinput, &typioparam);
	getBaseTypeAndTypmod(INT4OID, &typmod);

	/* Call input function with the specified string 'str' */
	result = InputFunctionCall(&fminfo, str, typioparam, typmod);
	return result;
}
}}}

#hr
''Datatype I/O functions''
*** OidInputFunctionCall [#b84b938b]

#geshi(c){{{
// 引数1:入力変換関数のOid
// 引数2:引数文字列
// 引数3:引数の型を示すOid
// 引数4:ドメイン(ドメインを使用していない場合は、-1を指定すれば良い)
// 戻り値:input関数の実行結果
extern Datum OidInputFunctionCall(Oid functionId, char *str,  Oid typioparam, int32 typmod);
}}}

- 指定された引数文字列を、入力変換関数を使って指定した型に変換する。

&label(sample){サンプル};
#geshi(c){{{
/*
 * select myfunc('100') + 1 => 101
 */
Datum
myfunc(PG_FUNCTION_ARGS)
{
	Datum 	result;
	Oid		typinput,
			typioparam;
	int32	typmod = -1;

	text *txt = PG_GETARG_TEXT_PP(0);
	char *str = text_to_cstring(txt);

	/* Get input function and parameter information of type INT4 */
	getTypeInputInfo(INT4OID, &typinput, &typioparam);
	getBaseTypeAndTypmod(INT4OID, &typmod);

	/* Call input function with the specified string 'str' */
	result = OidInputFunctionCall(typinput, str, typioparam, typmod);
	return result;
}
}}}
*** OutputFunctionCall [#p2fd7c99]

#geshi(c){{{
// 引数1:出力関数の関数情報
// 引数2:変換するデータ
// 戻り値:出力変換後の文字列
extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val);
}}}

- 指定されたデータを出力変換関数で文字列表現に変換する。


*** OidOutputFunctionCall [#j97eb5c1]

#geshi(c){{{
// 引数1:出力関数のOid
// 引数2:変換するデータ
// 戻り値:出力変換後の文字列
extern char *OidOutputFunctionCall(Oid functionId, Datum val);
}}}

- 指定されたデータを出力変換関数で文字列表現に変換する。

&label(sample){サンプル};
#geshi(c){{{
Datum
myfunc(PG_FUNCTION_ARGS)
{
	Oid typoutput;
	bool typisvarlena;
	char *outputstr;

	Datum arg1 = PG_GETARG_DATUM(0);

	/* Get output function information of type INT4 */
	getTypeOutputInfo(INT4OID, &typoutput, &typisvarlena);

	/* Call output function with the specified Datum data */
	outputstr = OidOutputFunctionCall(typoutput, arg1);
	elog(INFO, "converted to string '%s'", outputstr);
	PG_RETURN_TEXT_P(cstring_to_text(outputstr));
}
}}}
*** ReceiveFunctionCall [#h977f416]

#geshi(c){{{
// 引数1:
}}
// 引数2:
// 戻り値:
extern Datum ReceiveFunctionCall(FmgrInfo *flinfo, fmStringInfo buf,  Oid typioparam, int32 typmod);
}}}

- 説明

*** OidReceiveFunctionCall [#b0742073]

#geshi(c){{{
// 引数1:
// 引数2:
// 戻り値:
extern Datum OidReceiveFunctionCall(Oid functionId, fmStringInfo buf,  Oid typioparam, int32 typmod);
}}}

- 説明

*** SendFunctionCall [#n2eee1d5]

#geshi(c){{{
// 引数1:
// 引数2:
// 戻り値:
extern bytea *SendFunctionCall(FmgrInfo *flinfo, Datum val);
}}}

- 説明

*** OidSendFunctionCall [#k3174f6c]

#geshi(c){{{
// 引数1:
// 引数2:
// 戻り値:
extern bytea *OidSendFunctionCall(Oid functionId, Datum val);
}}}

- 説明

#hr

''Routines in fmgr.c''

#hr

''Routines in dfmgr.c''

#hr

''Fook function entry/exit''
* サンプルプログラム [#xcd4fcab]

#geshi(c){{
#include "parser/parse_func.h"
#include "fmgr.h"

// 関数呼び出しのサンプル
void test_func_invoke()
{
    FmgrInfo *fmgr = palloc(sizeof(FmgrInfo));
    FunctionCallInfoData fcinfo;

    // ここでは,サンプルのため決め打ちで関数のOidを指定している
    fmgr_info((Oid)1396, fmgr); // abs(int64); 
    List *names = list_make1(makeString("abs"));
    Oid argtypes[] = { INT8OID };

    // abs関数を探索
    Oid funcOid = LookupFuncName(names, list_length(names), argtypes, false);
    int64 arg0 = -100; // 引数

    FunctionCallInfoData fcinfo;
    InitFunctionCallInfoData(fcinfo, fmgr, 1, 0, NULL, NULL);
    fcinfo.isnull = false;     // 結果はnullでない
    fcinfo.argnull[0] = false; // 引数なnullでない
    fcinfo.arg[0] = Int64GetDatum(arg0); // 引数を設定
    if (funcOid != InvalidOid) {
        fmgr_info(funcOid, fmgr); // abs(int64); 

    // 関数の実行。fmgr_infoで実際に呼び出される関数ポインタがfcinfoにセットされている
    Datum result = FunctionCallInvoke(&fcinfo);
        InitFunctionCallInfoData(fcinfo, fmgr, 1, 0, NULL, NULL);
        fcinfo.isnull = false;     // 結果はnullでない
        fcinfo.argnull[0] = false; // 引数なnullでない
        fcinfo.arg[0] = Int64GetDatum(arg0); // 引数を設定

    // 結果Datumから値を取り出す
        // 関数の実行 No.1) fmgr_infoで実際に呼び出される関数ポインタがfcinfoにセットされている
        Datum result = FunctionCallInvoke(&fcinfo);

        // 結果Datumから値を取り出す
        elog(INFO, "abs(-100) = %ld", DatumGetInt64(result));

        // 関数の実行 No.2) 上の手続きを簡略化してくれるもっと便利な関数を使って実行してみる
        FunctionCall1Coll(fmgr, 0, Int64GetDatum(arg0));
        result = FunctionCallInvoke(&fcinfo);
        elog(INFO, "abs(-100) = %ld", DatumGetInt64(result));
    }

    // 関数の実行 No.3) 更に上の手続きをもっと簡略化してくれるもっと便利な関数を使って実行してみる
    result = OidFunctionCall1Coll(1396, 0, Int64GetDatum(arg0));
    elog(INFO, "abs(-100) = %ld", DatumGetInt64(result));
}

/* Output is the following:
------------------------------
INFO:  abs(-100) = 100
INFO:  abs(-100) = 100
INFO:  abs(-100) = 100
*/
}}
* 参考・関連 [#hbd36355]

- [[fmgr.h>https://doxygen.postgresql.org/fmgr_8h.html]] - on doxygen.postgresql.org
- [[fmgr.c>https://doxygen.postgresql.org/fmgr_8c.html]] - on doxygen.postgresql.org
- [[fmgrtab.h>https://doxygen.postgresql.org/fmgrtab_8h.html]] - on doxygen.postgresql.org
- [[CREATE FUNCTION>https://www.postgresql.jp/document/9.5/html/sql-createfunction.html]] - PostgreSQL 9.5文書
- [[49.37. pg_proc>https://www.postgresql.jp/document/9.5/html/catalog-pg-proc.html]] - PostgreSQL 9.5文書

&size(12){&color(white,orange){ 関連 };};

#related
* コメント [#cd6bf6cf]

#comment


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP