Hi Steve,]
(Claudio - are you reading this too? I can do the fixes, but maybe you
want to take a look yourself. Thanks)
> I've been banging my head against a wall all afternoon trying to get
> lib_mysqludf_str to work. Firstly it was Ubuntu's apparmor profiles
> preventing mysql from loading a library from /usr/local/lib. Then the
> configure/make scripts packaged with lib_mysqludf_str produced a
> library that didn't contain any functions, but the (much simpler)
Sorry to hear that. I don't know too much about the autotools autoconf
stuff. I just went ahead and compiled using a gcc line like you.
> mysql> select str_ucwords(string) from test;
> ERROR:
> str_ucwords requires one string argument
I noticed an error in the c source file. In the declarations in the
top, i suspect that this set:
DLLEXP
my_bool str_ucwords_init(UDF_INIT *initid, UDF_ARGS *args, char
*message);
DLLEXP
void str_ucfirst_deinit(UDF_INIT *initid);
DLLEXP
char *str_ucfirst(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *null_value, char *error);
Should have been:
DLLEXP
my_bool str_ucfirst_init(UDF_INIT *initid, UDF_ARGS *args, char
*message);
DLLEXP
void str_ucfirst_deinit(UDF_INIT *initid);
DLLEXP
char *str_ucfirst(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *null_value, char *error);
(So, the init function for ucwords was accidentally declared twice.
This would prevent the str_ucfirst function from being available)
Second, I found an error in the argument checking of ucwords:
my_bool str_ucwords_init(UDF_INIT *initid, UDF_ARGS *args, char
*message)
{
/* make sure user has provided exactly one string argument */
if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT ||
args->args[0] == NULL)
{
strcpy(message,"str_ucwords requires one string argument");
return 1;
}
/* str_ucwords() will not be returning null */
initid->maybe_null=0;
return 0;
}
Well, the check that goes:
args->args[0] == NULL
is wrong. In the init function,
args->args[0] == NULL
means that the argument is not a constant.
Clearly, this is wrong. It should be ok to pass variable values.
I found more problems: the main function
char *str_ucwords(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *res_length,
char *null_value, char *error)
does not check if the argument has a NULL value. IMO, it should return
NULL in this case.
Second, this line:
// copy the argument string into result
strncpy(result, args->args[0], args->lengths[0]);
is an accident waiting to happen. The result buffer is 255 bytes
long.
The code currently does not check the length of the argument.
So, if args->args[0] > 255 then we will likely get a crash of the
server.
IMO, what should happen is that the init function should allocate
sufficient memory:
my_bool str_ucwords_init(UDF_INIT *initid, UDF_ARGS *args, char
*message)
{
/* make sure user has provided exactly one string argument */
if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT)
{
strcpy(message,"str_ucwords requires one string argument");
return 1;
}
if (! (initid->ptr = malloc(args->lengths[0])) )
{
strcpy(message,"str_ucwords could not allocate memory");
return 1;
}
return 0;
}
and in the row level function, this memory should be used rather than
result.