Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebird UDF in Linux crashes server when set to return by reference

I'm trying to create some simple UDF for firebird in Linux (in C compiled with GCC). The problem is, that when I set "Return mechanism" to be "by reference", when I call the function, the server crashes. When it's "by value", there's no problem.

Here are the functions I try to write in C:

This one works:

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

But this one crashes the server when called:

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

Here are the DDLs:

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

I call second function with:

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

The firebird server crashes and the only error message i've got is:

Statement failed, SQLSTATE = -902
Error reading data from the connection.

Could somebody advise? Any help will be appreciated!

The only info I forgot to provide is the way I'm compiling .so file (may be the key is here):

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
like image 533
Dusty Raven Avatar asked Nov 03 '22 03:11

Dusty Raven


1 Answers

AFAIK, Firebird will try to free both the argument and return value after the call (as it doesn't know what you do inside the function and you have declared your return value as FREE_IT), so you need to allocate the space for return value before returning it. You must allocate the memory with ib_util_malloc() call, so it would look something like (haven't even tried to compile it but it should give you the general idea):

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

EDIT: I found an example from the Firebird built in UDF lib:

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;
}
like image 92
Zdeslav Vojkovic Avatar answered Nov 15 '22 05:11

Zdeslav Vojkovic