c++mysqlmysql-udf

MySQL UDF returning STRING overlaps the data


As it's my first time to write UDF so I tried to write simple UDF to return the same argument which is passed to UDF.

code as below:

#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <cstring>
#include <mysql.h>
#include <ctype.h>
#include <my_global.h>
#include <my_sys.h>
using namespace std;


extern "C" my_bool get_arg_init(UDF_INIT *initid, UDF_ARGS *args,
                               char *message)
{
    if ( ( args->arg_count != 1 ) || ( args->arg_type[0] != STRING_RESULT ) )
     {
      strcpy( message, "Wrong argument type." );
      return 1;
     }

    return 0;
}

extern "C" void get_arg_deinit(UDF_INIT *initid)
{
    //nothing to free here
}

extern "C" char *get_arg(UDF_INIT *initid, UDF_ARGS *args,
          char *result, unsigned long *length,
          char *is_null, char *error)
{
    std::string str = args->args[0]; // get the first argument passed
    memcpy(result, str.c_str(), str.size()); // copy argument value into result buffer
    *length = str.size(); // set length

    return result;//return the same argument
}

My table having data as;

SELECT c_name FROM tbl;

This will return data as:

# c_name
amogh bharat shah
viraj

If I execute query using UDF :

SELECT get_arg(c_name) FROM tbl;

This returns:

# get_arg(c_name)
amogh bharat shah
viraj bharat shah

It looks like while second row first 5 characters are replaced with actual row data other part of string is garbage from first row.

Why this happens? and what should I change in function to avoid overlapping of string?


Solution

  • The strings passed to your function are not necessarily null terminated, from https://dev.mysql.com/doc/refman/8.0/en/udf-arguments.html:

    Do not assume that the string is null-terminated.

    Constructing a std::string from a non-null terminated character string is undefined behaviour, in this case I guess the buffer was initially 0 filled so the string ends at the end of the longest string which has ever been put into the buffer.

    The correct code is:

    std::string str( args->args[0], args->lengths[0] );
    

    Or skip creating the unnecessary copy in the std::string:

    memcpy(result, args->args[0], args->lengths[0]);
    *length = args->lengths[0];