复制PostgreSQL内部的C函数并将其加载为用户定义函数

4
我想创建修改版的C函数,该函数位于。作为开端,我认为可以编译并将作为用户定义函数加载。
我的操作步骤如下:
1. 将添加到中(如此处所述); 2. 在中将重命名为; 3. 按照文档中的说明编译(无错误、无警告)(cc -fpic -Ipg_config --includedir-server-c numeric.c,然后cc -shared -o numeric.so numeric.o); 4. 在PostgreSQL中创建一个函数:
create or replace function int2_avg_accum2(bigint[], smallint)
  returns bigint[] as
'/usr/lib/postgresql/9.1/lib/numeric', 'int2_avg_accum2'
  language c
  cost 1;
alter function int2_avg_accum2(bigint[], smallint)
  owner to postgres;

当我尝试运行select int2_avg_accum2(array[1::bigint,1],1::smallint);时,在pgAdmin中只收到消息:"Do you want to attempt to reconnect to the database?"。没有其他消息或错误。
当我调用该函数时,我在/var/log/postgresql/postgresql-9.1-main.log中看到以下内容:
2013-12-03 09:52:02 CET LOG:  server process (PID 3366) was terminated by signal 11: Segmentation fault
2013-12-03 09:52:02 CET LOG:  terminating any other active server processes
2013-12-03 09:52:02 CET WARNING:  terminating connection because of crash of another server process
2013-12-03 09:52:02 CET DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
2013-12-03 09:52:02 CET HINT:  In a moment you should be able to reconnect to the database and repeat your command.
2013-12-03 09:52:02 CET LOG:  all server processes terminated; reinitializing
2013-12-03 09:52:02 CET LOG:  database system was interrupted; last known up at 2013-12-03 09:50:53 CET
2013-12-03 09:52:02 CET LOG:  database system was not properly shut down; automatic recovery in progress
2013-12-03 09:52:02 CET LOG:  record with zero length at 0/B483EA0
2013-12-03 09:52:02 CET LOG:  redo is not required
2013-12-03 09:52:03 CET LOG:  autovacuum launcher started
2013-12-03 09:52:03 CET LOG:  database system is ready to accept connections

我需要做些什么才能获得 int2_avg_accum 的工作副本?


你是否编译了整个numeric.c文件,还是只提取了int2_avg_accum函数?询问是否要重新连接的原因很可能是后端正在发生段错误 - 在postgresql后端日志或/var/log/messages中有关此方面的任何消息吗? - harmic
@harmic 我编译了整个 numeric.c。你是正确的,后端正在发生段错误。我添加了日志详细信息。还有其他提示吗? - Tomas Greif
1个回答

3
psql客户端询问是否要重新连接的原因是后端正在崩溃,根据评论所述。
可以从这样的崩溃中收集核心转储,并使用调试器(例如gdb)检查它以找出确切的崩溃位置。然而,我最好的猜测是,它之所以崩溃是因为你已经拿了一个被编写为postgresql核心组件的大文件,将其单独编译,并尝试作为扩展模块加载。
numeric.c文件包含大量函数、静态变量和数据结构,你只是试图复制其中的一个。所有这些函数、变量等已经存在于运行的postgresql系统中。当你编译你的numeric.c版本并加载它时,你添加的新函数将引用你库中的函数和变量,而不是使用主postgresql程序中的函数和变量。它可能引用未正确初始化的数据结构,导致崩溃。
我建议你从一个空文件开始,仅复制numeric.c中的int2_avg_accum函数(按你所做的重命名)。如果该函数调用postgresql中的其他函数或引用变量,它将使用主postgresql二进制文件中的函数和变量,这正是你想要的。你可以#include原始的numeric.h来获取所有外部函数的声明。
在将函数定义为动态加载模块时,还有一些与内部函数定义方式不同的地方:
你需要添加宏来指定你正在使用V1调用约定: PG_FUNCTION_INFO_V1(int2_avg_accum2); 如果缺失,这也会导致崩溃,因为postgresql将假定版本0的调用约定,这与函数定义不匹配!
正如你所指出的,你必须包含PG_MODULE_MAGIC。
对我有效的完整文件是:
#include "postgres.h"
#include "fmgr.h"
#include "utils/array.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

typedef struct Int8TransTypeData
{
    int64       count;
    int64       sum;
} Int8TransTypeData;

PG_FUNCTION_INFO_V1(int2_avg_accum2);

Datum
int2_avg_accum2(PG_FUNCTION_ARGS)
{
    ArrayType  *transarray;
    int16       newval = PG_GETARG_INT16(1);
    Int8TransTypeData *transdata;

    /*
     * If we're invoked as an aggregate, we can cheat and modify our first
     * parameter in-place to reduce palloc overhead. Otherwise we need to make
     * a copy of it before scribbling on it.
     */
    if (AggCheckCallContext(fcinfo, NULL))
        transarray = PG_GETARG_ARRAYTYPE_P(0);
    else
        transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

    if (ARR_HASNULL(transarray) ||
        ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
        elog(ERROR, "expected 2-element int8 array");

    transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
    transdata->count++;
    transdata->sum += newval;

    PG_RETURN_ARRAYTYPE_P(transarray);
}

编译时使用的:

gcc -I/usr/pgsql-9.2/include/server -fPIC -c my_avg_accum.c
gcc -shared -o my_avg_accum.so my_avg_accum.o

我曾在Centos 6上使用Postgresql 9.2。根据您的设置,您可能需要调整路径。


谢谢。我尝试编译只有 int2_avg_accum 的代码。它可以成功编译,但仍然出现段错误。 - Tomas Greif
我认为这是由于调用约定的原因。只要我声明PG_FUNCTION_INFO_V1(int2_avg_accum2),它对我有效。我已经更新了有关此事的更多详细信息。 - harmic
很棒,现在它可以工作了!添加 PG_FUNCTION_INFO_V1(int2_avg_accum2); 就是诀窍。 - Tomas Greif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接