MySQL UDF отвечает с мусорными символами, добавленными к возвращенной строке - PullRequest
5 голосов
/ 08 мая 2019

Итак, я создал UDF, который принимает 2 строки и объединяет их.

Мой UDF: // concat_kv.c

#include <my_global.h>
#include <my_sys.h>
#include <mysql.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

typedef unsigned long ulong;

my_bool concat_kv_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
    if(args->arg_count != 2 || args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT) {
        strcpy(message, "concat_kv(): Requires 2 string parameters: Key - Value.");
        return 1;
    }
    return 0;
}

char *concat_kv(UDF_INIT *initid, UDF_ARGS *args, char *result, ulong *length, char *is_null, char *error) {
    char *key = (char*)calloc(strlen(args->args[0]), sizeof(char));
    char *value = (char*)calloc(strlen(args->args[1]), sizeof(char));
    char *res = (char *)calloc(strlen(args->args[0]) + strlen(args->args[1]) + 2, sizeof(char));
    int len = strlen(args->args[0]) + strlen(args->args[1]) + 2;
    key = args->args[0];
    value = args->args[1];
    strcat(res, key);
    strcat(res, " ");
    strcat(res, value);
    res[len-1] = '\0'; // Terminating character...
    return res;
}

void concat_kv_deinit(UDF_INIT *initid) {
}

Скомпилировал файл как:

gcc $(mysql_config --cflags) -shared concat_kv.c -o concat_kv.so  

Переместил файл concat_kv.so в /usr/lib/mysql/plugins/.

Создал функцию в mysql как:

CREATE FUNCTION concat_kv RETURNS STRING SONAME 'concat_kv.so';  

Затем делаем:

SELECT concat_kv("Aditya", "Singh") as conc;

Ожидаемый результат:

| conc |
--------
| "Aditya Singh" |

Но получаю неожиданный вывод как:

mysql> SELECT concat_kv("Aditya", "Singh") as conc;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| conc 
|
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Aditya Singh            �T 
|+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

Я получаю что-то непечатаемое после конкатенированной строки. Некоторые значения мусора добавляются после строки.

1 Ответ

6 голосов
/ 10 мая 2019

Ваша функция concat_kv должна выглядеть примерно так:

char *concat_kv(UDF_INIT *initid, UDF_ARGS *args, char *result, ulong *length, char *is_null, char *error) {
    char *key = args->args[0];
    char *value = args->args[1];
    strcpy(result, key);
    strcat(result, " ");
    strcat(result, value);
    *length = strlen(result);
    return result;
}

Некоторые замечания:

  • параметр результата должен использоваться для результата

  • длина должна быть возвращена

  • длина должна быть без завершающего нулевого символа, см. Здесь: https://dev.mysql.com/doc/refman/8.0/en/udf-return-values.html

  • ключ и значение просто используются из аргументов и копируются через strcpy / strcat в результат

Демо

concat_kv in MySQL on Ubuntu

Динамическое выделение памяти

Как уже указывалось в комментариях, существует опасность переполнения буфера, если размер предварительно выделенного буфера превышен.

Утечка памяти

Если бы мы динамически распределяли память в concat_kv (), мы бы привели к утечке памяти, поскольку память запрашивается каждый раз, когда вызывается пользовательская функция, и больше никогда не освобождается.

Решением является использование функций concat_kv_init () и concat_kv_deinit (), которые вызываются MySQL непосредственно до и после вызова concat_kv ().

Вот цитата из документации MySQL:

MySQL передает буфер в функцию xxx (), используя параметр результата. Этот буфер достаточно длинный, чтобы вместить 255 символов, которые могут быть многобайтовыми символами. Функция xxx () может сохранить результат в этом буфере, если он подходит, и в этом случае возвращаемое значение должно быть указателем на буфер. Если функция сохраняет результат в другом буфере, она должна вернуть указатель на этот буфер.

Если ваша строковая функция не использует предоставленный буфер (например, если ей нужно вернуть строку длиной более 255 символов), вы должны выделить место для вашего собственного буфера с помощью malloc () в вашей функции xxx_init () или функцию xxx () и освободите ее в функции xxx_deinit ().

см. https://dev.mysql.com/doc/refman/8.0/en/udf-return-values.html

Пример с динамическим выделением памяти

Так что, если мы не можем гарантировать, что размер результата будет большим, чем предварительно выделенный буфер, нам нужно выделить память в concat_kv_init () и освободить ее в concat_kv_deinit ().

my_bool concat_kv_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
    if(args->arg_count != 2 || args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT) {
        strcpy(message, "concat_kv(): Requires 2 string parameters: Key - Value.");
        return 1;
    }
    ulong length = strlen(args->args[0]) + strlen(args->args[1]) + 2;
    initid->ptr = (char *)malloc(length);
    return 0;
}

char *concat_kv(UDF_INIT *initid, UDF_ARGS *args, char *result, ulong *length, char *is_null, char *error) {
    char *key = args->args[0];
    char *value = args->args[1];
    strcpy(initid->ptr, key);
    strcat(initid->ptr, " ");
    strcat(initid->ptr, value);
    *length = strlen(initid->ptr);
    return initid->ptr;
}

void concat_kv_deinit(UDF_INIT *initid) {
    free(initid->ptr);
    initid->ptr = NULL;
}
...