PostgreSQL C风格的自定义函数FuncCallContext结构体描述及示例

1.FuncCallContext详细说明

/*-------------------------------------------------------------------------
 *      函数调用上下文struct(SRF为Set Returning Functions)
 *-------------------------------------------------------------------------
 *
 * 此结构保存设置返回函数的函数上下文。
 * 使用fn_extra在调用之前保存一个指针
 * 此struct适用于函数需要返回多行时使用
 */
typedef struct FuncCallContext
{
    /*
     * 函数以前调用过的次数
     * call_cntr由SRF_FIRSTCALL_INIT()初始化为0
     * 并在每次调用SRF_RETURN_NEXT()时递增
     */
    uint64      call_cntr;

    /*
     * [可选参数]最大调用数量
     * max_calls只是为了方便起见设置它是可选的.如果没有设置
     * 你必须提供替代方法来知道什么时候函数已经完成
     * 这个值类似select中的limit
     */
    uint64      max_calls;

    /*
     * 可选指针到插槽(已过时,不要再使用)
     * 已过时,只是为了兼容以前的版本
     */
    TupleTableSlot *slot;

    /*
     * [可选参数]用户提供的上下文指针
     * uuser_fctx用作指向您自己定义的指针
     * 以便在函数调用之间保留任意上下文信息
     */
    void       *user_fctx;

    /*
     * [可选参数]指向包含属性类型输入元数据的struct的指针
     * attinmeta用于返回元组(即复合数据类型)
     * 并且在返回基本数据类型时不使用
     * 仅当您打算使用BuildTupleFromCStrings()创建返回元组时才需要
     */
    AttInMetadata *attinmeta;

    /*
     * 使用多次调用必须的structures内存上下文
     * multi_call_memory_ctx在调用SRF_FIRSTCALL_INIT()时设置
     * 并由SRF_RETURN_DONE()用于清理.  
     * 对于要在SRF中多次调用中重用的内存来说这是最合适的内存上下文.
     */
    MemoryContext multi_call_memory_ctx;

    /*
     * [可选参数]指向包含元组描述的struct的指针
     * tuple_desc在返回元组(即复合数据类型)时使用
     * 只有在要使用heap_form_tuple()而不是BuildTupleFromCStrings()构建元组时才需要使用
     * 请注意,TupleDesc指针应该先运行BlessTupleDesc()
     */
    TupleDesc   tuple_desc;

} FuncCallContext;

2.AttInMetadata示例,windows或Linux均可,如果不能编译,请添加必须的头文件

#include "postgres.h"
#include "fmgr.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

PGDLLEXPORT Datum retcomposite(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(retcomposite);

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

    /* stuff done only on the first call of the function */
    if (SRF_IS_FIRSTCALL()) {
        MemoryContext oldcontext;
        /* create a function context for cross-call persistence*/
        funcctx = SRF_FIRSTCALL_INIT();
        /* switch to memory context appropriate for multiple function calls */
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
        /*设置返回的记录集数量 */
        funcctx->max_calls = PG_GETARG_UINT32(0);
        /* Build a tuple descriptor for our result type */
        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")));
        /* generate attribute metadata needed later to produce tuples from raw C strings */
        attinmeta = TupleDescGetAttInMetadata(tupdesc);
        funcctx->attinmeta = attinmeta;
        MemoryContextSwitchTo(oldcontext);
        //elog(NOTICE, "%d", 0);
    }
    //elog(NOTICE, "%s", "x");
    /* stuff done on every call of the function */
    funcctx = SRF_PERCALL_SETUP();
    call_cntr = funcctx->call_cntr;
    max_calls = funcctx->max_calls;
    attinmeta = funcctx->attinmeta;
    if (call_cntr < max_calls) {  /* do when there is more left to send */
        char **values;
        HeapTuple tuple;
        Datum result;
        /* Prepare a values array for building the returned
        tuple. This should be an array of C strings which
        will be processed later by the type input functions. */
        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));
        /* build a tuple */
        tuple = BuildTupleFromCStrings(attinmeta, values);
        /* make the tuple into a datum */
        result = HeapTupleGetDatum(tuple);
        /* clean up (this is not really necessary) */
        pfree(values[0]);
        pfree(values[1]);
        pfree(values[2]);
        pfree(values);
        SRF_RETURN_NEXT(funcctx, result);
    } else {  /* do when there is no more left */
        SRF_RETURN_DONE(funcctx);
    }
}

3.函数定义

其中retcomposite的第1个参数是调用次数(返回的记录数量)

create type __retcomposite as (f1 integer, f2 integer, f3 integer);
create function retcomposite(integer, integer)
    returns setof __retcomposite
as 'pg_kmcb', 'retcomposite'
language C immutable strict;

或者使用out参数

create function retcomposite(in integer, in integer,out f1 integer, out f2 integer, out f3 integer)
    returns setof record
as 'pg_kmcb', 'retcomposite'
language C immutable strict;
注意:函数定义只能使用其中的一个

4.定义control,这里我编译的扩展是pg_kmcb.so或pg_kmcb.dll

comment = '自定义C风格的函数'
default_version = '1.0'
module_pathname = '$libdir/pg_kmcb'
relocatable = true

5.创建扩展

以postgres用户连接到指定的数据库,然后运行

create extension pg_kmcb;

6.使用方法

返回100条一样的记录

select * from retcomposite(100,1);

你可能感兴趣的:(PostgreSQL二次开发)