在Linux系统下,当将Firebird UDF设置为通过引用返回时,会导致服务器崩溃。

5
我正在尝试在Linux上使用GCC编译一些简单的firebird UDF(用户定义函数)。问题是,当我将“返回机制”设置为“按引用”时,调用该函数会导致服务器崩溃。当它是“按值”时,就没有问题。
以下是我尝试用C编写的函数:
这个可以工作:
double round(double *); 
double round(val)
double *val;
{
    *val = *val * 100;
    *val = (*val>= 0) ? (long)(*val + 0.5) : (long)(*val - 0.5);
    *val = *val / 100;
    return *val;
}

但是这个函数在调用时会导致服务器崩溃:

char * proper_case(str)
char * str;
{
    return str;
}

以下是DDL:

DECLARE EXTERNAL FUNCTION "ROUND"
    DOUBLE PRECISION
RETURNS DOUBLE PRECISION BY VALUE
ENTRY_POINT 'round' MODULE_NAME 'my_udfs.so';

DECLARE EXTERNAL FUNCTION PROPCASE
    CSTRING(10000)
RETURNS CSTRING(10000) FREE_IT
ENTRY_POINT 'proper_case' MODULE_NAME 'my_udfs.so';

我使用以下代码调用第二个函数:

select propcase('abrakadabra') from rdb$database;

火鸟服务器崩溃,我收到的唯一错误信息是:
Statement failed, SQLSTATE = -902
Error reading data from the connection.

能有人给些建议吗?非常感谢!

我忘记提供的唯一信息是我编译.so文件的方式(这可能是关键):

gcc -c -O -fpic my_udf.c
ld -G my_udf.o -lm -lc -o my_udf.so
cp my_udf.so /usr/lib/firebird/2.1/UDF/my_udfs.so

我建议查看Interbase 6.0开发人员指南的第6章(可从http://www.firebirdsql.org/en/reference-manuals/获取),我还没有编写UDF,但我认为您正在错误地分配内存。 - Mark Rotteveel
查看一些随 Firebird 发送的 UDF 代码可能也会有所帮助: http://svn.code.sf.net/p/firebird/code/firebird/trunk/src/extlib/ib_udf.cpp - Mark Rotteveel
@MarkRotteveel - 我从Firebird SVN复制了以下函数:pChar EXPORT IB_UDF_lower(const char * s) - 结果是相同的。我唯一做出的更改是删除了EXPORT这个词,因为GCC不认识它。 - Dusty Raven
我相信在大多数平台上,EXPORT 的定义本来就是什么也不做。我建议您还应该通过Firebird-devel邮件列表https://lists.sourceforge.net/lists/listinfo/firebird-devel联系Firebird核心开发人员。 - Mark Rotteveel
2个回答

2
据我所知,Firebird将尝试在调用后释放参数和返回值(因为它不知道您在函数内部做了什么,并且您已经声明了返回值为FREE_IT),因此您需要在返回之前为返回值分配空间。您必须使用ib_util_malloc()调用来分配内存,所以它看起来应该是这样的(我甚至还没有尝试编译它,但它应该能给您一个大致的想法):
char * proper_case(str)
char * str;
{
    char* ret = (char*)ib_util_malloc(strlen(str) + 1);
    strcpy(ret, str); // or run the actual logic here
    return ret;
}

编辑:我在Firebird内置的UDF库中找到了一个示例:

pChar EXPORT IB_UDF_lower(const char *s)
{
if (!s) return 0;

char* buf = (char *) ib_util_malloc(strlen(s) + 1);
char* p = buf;
while (*s) {
    if (*s >= 'A' && *s <= 'Z') {
        *p++ = *s++ - 'A' + 'a';
    }
    else
        *p++ = *s++;
}
*p = '\0';

return buf;
}

也许我错过了一些关键的东西,但是工作和崩溃函数之间唯一显著的区别是前者必须返回“按值传递”,而后者则是“按引用传递”。 - Dusty Raven
@Mark - 没有“FREE_IT”,结果是一样的。 - Dusty Raven
你确定你正在构建的头文件/库的版本与FB服务器的版本匹配吗?除非你链接静态库(我认为你没有),否则这并不重要,但我想不出其他原因。 - Zdeslav Vojkovic
@ZdeslavVojkovic - 我刚试图创建一个只有一个函数的新.so文件,从UDF库中复制 - pChar EXPORT IB_UDF_lower(const char *s)... 但结果仍然是一样的 - 服务器崩溃了。 - Dusty Raven
1
@DustyRaven 我会说这表明你的设置(编译选项等)未正确设置,因为那个UDF在Firebird中是可以工作的。 - Mark Rotteveel
显示剩余4条评论

0

谢谢Mark,你是对的。编译/链接出了问题。

这不完全是一个答案,但我是如何解决这个问题的。

我使用以下命令编译UDF:

gcc -c -O -fpic my_udf.c
ld -G my_udf.o -lm -lc -o my_udf.so
cp my_udf.so /usr/lib/firebird/2.1/UDF/my_udfs.so

没有错误,火鸟服务器崩溃时也没有错误状态。 所以我打开了Python并尝试了以下操作:

from ctypes import *
libc = CDLL("path/to/my_udf.so")
libc.IB_UDF_lower("abrakadabra")

Python 抛出了关于不存在函数 "ib_util_malloc" 的错误。

所以我将其替换为 "malloc",一切都正常工作了。

我仍然不知道我做错了什么,但由于 GCC 没有给我任何错误,我想这可能是一个链接问题。


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