扩展 MySQL 8.4  /  向 MySQL 添加函数  /  添加可加载函数

6.2 添加可加载函数

MySQL 的可加载函数接口提供了以下特性和功能:

  • 函数可以返回字符串、整数或实数值,并且可以接受相同类型的参数。

  • 您可以定义一次对单行进行操作的简单函数,或者对行组进行操作的聚合函数。

  • 为函数提供的信息使它们能够检查传递给它们的参数的数量、类型和名称。

  • 您可以告诉 MySQL 在将参数传递给函数之前将参数强制转换为给定类型。

  • 您可以指示函数返回 NULL 或发生错误。

为了使可加载函数机制起作用,函数必须用 C++ 编写,并且您的操作系统必须支持动态加载。MySQL 源代码分发版包含一个名为 sql/udf_example.cc 的文件,该文件定义了五个可加载函数接口函数。请查阅此文件以了解可加载函数调用约定是如何工作的。 include/mysql_com.h 头文件定义了与可加载函数相关的符号和数据结构,尽管您不需要直接包含此头文件;它由 mysql.h 包含。

可加载函数包含成为正在运行的服务器一部分的代码,因此,当您编写可加载函数时,您受任何适用于编写服务器代码的约束的约束。例如,如果您尝试使用 libstdc++ 库中的函数,可能会遇到问题。这些约束可能会在服务器的未来版本中发生变化,因此服务器升级可能会要求对最初为旧服务器编写的可加载函数进行修改。有关这些约束的信息,请参阅 MySQL 源代码配置选项处理 MySQL 编译问题

为了能够使用可加载函数,您必须动态链接 mysqld。如果您想使用需要访问 mysqld 中的符号的可加载函数(例如, sql/udf_example.cc 中的 metaphone 函数使用 default_charset_info),您必须使用 -rdynamic 选项链接程序(请参阅 man dlopen)。

对于您想要在 SQL 语句中使用的每个函数,您都应该定义相应的 C++ 函数。在以下讨论中,名称 xxx 用于示例函数名称。为了区分 SQL 和 C++ 的用法, XXX()(大写)表示 SQL 函数调用,而 xxx()(小写)表示 C++ 函数调用。

注意

使用 C++ 时,将您的 C 函数封装在此结构中

extern "C" { ... }

这确保您的 C++ 函数名称在完成的函数中保持可读性。

可加载函数接口函数

以下列表描述了您编写的用于实现名为 XXX() 的函数的接口的 C++ 函数。主函数 xxx() 是必需的。此外,可加载函数至少需要以下列表中描述的其他函数之一,原因在 可加载函数安全预防措施 中进行了讨论。

  • xxx()

    主函数。这是计算函数结果的地方。SQL 函数数据类型与 C++ 函数的返回类型之间的对应关系如下所示。

    SQL 类型 C++ 类型
    STRING char *
    INTEGER long long
    REAL double

    还可以声明一个 DECIMAL 函数,但值将作为字符串返回,因此您应该像编写 STRING 函数一样编写函数。 ROW 函数未实现。

  • xxx_init()

    xxx() 的初始化函数。如果存在,它可用于以下目的:

    • 检查传递给 XXX() 的参数数量。

    • 验证参数是否为必需类型,或者,在调用主函数时告诉 MySQL 将参数强制转换为必需类型。

    • 为主函数分配任何所需的内存。

    • 指定结果的最大长度。

    • 指定(对于 REAL 函数)结果中小数点后的最大位数。

    • 指定结果是否可以为 NULL

  • xxx_deinit()

    xxx() 的去初始化函数。如果存在,它应该释放初始化函数分配的任何内存。

当 SQL 语句调用 XXX() 时,MySQL 会调用初始化函数 xxx_init() 以便它执行任何必要的设置,例如参数检查或内存分配。如果 xxx_init() 返回错误,MySQL 会使用错误消息中止 SQL 语句,并且不会调用主函数或去初始化函数。否则,MySQL 会为每一行调用一次主函数 xxx()。在处理完所有行之后,MySQL 会调用去初始化函数 xxx_deinit(),以便它可以执行任何必要的清理工作。

对于像 SUM() 一样工作的聚合函数,您还必须提供以下函数:

  • xxx_clear()

    重置当前聚合值,但不将参数插入为新组的初始聚合值。

  • xxx_add()

    将参数添加到当前聚合值。

MySQL 处理聚合可加载函数的方式如下:

  1. 调用 xxx_init() 以便聚合函数分配它需要的任何用于存储结果的内存。

  2. 根据 GROUP BY 表达式对表进行排序。

  3. 为每个新组中的第一行调用 xxx_clear()

  4. 为属于同一组的每一行调用 xxx_add()

  5. 当组发生变化或在处理完最后一行之后,调用 xxx() 以获取聚合的结果。

  6. 重复步骤 3 到 5,直到处理完所有行。

  7. 调用 xxx_deinit() 以便函数释放它分配的任何内存。

所有函数必须是线程安全的。这不仅包括主函数,还包括初始化和去初始化函数,以及聚合函数所需的额外函数。此要求的后果是您不允许分配任何更改的全局或静态变量!如果您需要内存,您必须在 xxx_init() 中分配它,并在 xxx_deinit() 中释放它。

简单函数的可加载函数调用序列

本节描述了您必须定义的用于创建简单可加载函数的不同接口函数。有关 MySQL 调用这些函数的顺序的信息,请参阅 可加载函数接口函数

xxx() 函数应按本节所示进行声明。请注意,返回类型和参数不同,具体取决于您在 CREATE FUNCTION 语句中声明 SQL 函数 XXX() 返回 STRINGINTEGER 还是 REAL

对于 STRING 函数

char *xxx(UDF_INIT *initid, UDF_ARGS *args,
          char *result, unsigned long *length,
          char *is_null, char *error);

对于 INTEGER 函数

long long xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);

对于 REAL 函数

double xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);

DECIMAL 函数返回字符串值,声明方式与 STRING 函数相同。 ROW 函数未实现。

像这样声明初始化和去初始化函数:

bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);

void xxx_deinit(UDF_INIT *initid);

initid 参数传递给所有三个函数。它指向一个 UDF_INIT 结构,该结构用于在函数之间传递信息。 UDF_INIT 结构成员如下所示。初始化函数应该填写它希望更改的任何成员。(要使用成员的默认值,请保持它不变。)

  • bool maybe_null

    如果 xxx() 可以返回 NULL,则 xxx_init() 应该将 maybe_null 设置为 1。如果任何参数声明为 maybe_null,则默认值为 1

  • unsigned int decimals

    小数点右侧的十进制数字数。默认值为传递给主函数的参数中的最大十进制数字数。例如,如果函数传递了 1.341.3451.3,则默认值为 3,因为 1.345 有 3 个十进制数字。

    对于没有固定小数位数的参数,decimals 值设置为 31,这比允许的 DECIMALFLOATDOUBLE 数据类型的最大小数位数多 1。此值在 mysql_com.h 头文件中作为常量 NOT_FIXED_DEC 可用。

    在以下情况下,decimals 值为 31 用于参数:例如,FLOATDOUBLE 列声明时没有显式的小数位数(例如,FLOAT 而不是 FLOAT(10,3)),以及浮点常量,如 1345E-3。它也用于字符串和其他可能在函数内部转换为数值形式的非数值参数。

    decimals 成员初始化的值只是一个默认值。它可以在函数内部更改以反映执行的实际计算。默认值由参数的最大小数位数决定。如果参数之一的小数位数为 NOT_FIXED_DEC,则使用该值作为 decimals 的值。

  • 无符号整数 max_length

    结果的最大长度。默认的 max_length 值取决于函数的结果类型。对于字符串函数,默认值为最长参数的长度。对于整数函数,默认值为 21 位。对于实数函数,默认值为 13 加上由 initid->decimals 指示的小数位数。(对于数值函数,长度包括任何符号或小数点字符。)

    如果要返回一个 blob 值,可以将 max_length 设置为 65KB 或 16MB。这部分内存不会分配,但该值用于在需要临时存储数据时决定使用哪种数据类型。

  • 字符 *ptr

    函数可以将其用于自身目的的指针。例如,函数可以使用 initid->ptr 在彼此之间传递分配的内存。 xxx_init() 应该分配内存并将其赋予此指针。

    initid->ptr = allocated_memory;

    xxx()xxx_deinit() 中,请参考 initid->ptr 来使用或释放内存。

  • 布尔 const_item

    xxx_init() 应该将 const_item 设置为 1(如果 xxx() 始终返回相同的值)或设置为 0(否则)。

聚合函数的可加载函数调用序列

本节介绍在创建聚合可加载函数时需要定义的不同接口函数。有关 MySQL 调用这些函数的顺序的信息,请参见 可加载函数接口函数

  • xxx_reset()

    当 MySQL 在一个新组中找到第一行时,就会调用此函数。它应该重置任何内部汇总变量,然后使用给定的 UDF_ARGS 参数作为该组的内部汇总值的第一个值。声明 xxx_reset() 如下

    void xxx_reset(UDF_INIT *initid, UDF_ARGS *args,
                   char *is_null, char *error);

    xxx_reset() 在 MySQL 8.4 中不需要也不使用,在 MySQL 8.4 中,可加载函数接口使用 xxx_clear() 而不是 xxx_reset()。但是,如果您希望您的函数与旧版本的服务器一起使用,可以同时定义 xxx_reset()xxx_clear()。(如果您确实同时包含了这两个函数,在许多情况下,xxx_reset() 函数可以在内部通过调用 xxx_clear() 来重置所有变量,然后调用 xxx_add() 来添加 UDF_ARGS 参数作为组中的第一个值。)

  • xxx_clear()

    当 MySQL 需要重置汇总结果时,就会调用此函数。它会在每个新组的开始被调用,但也可以在没有匹配行的查询中被调用来重置值。声明 xxx_clear() 如下

    void xxx_clear(UDF_INIT *initid, char *is_null, char *error);

    在调用 xxx_clear() 之前,is_null 被设置为指向 CHAR(0)

    如果出现错误,您可以将值存储在 error 参数指向的变量中。 error 指向一个单字节变量,而不是字符串缓冲区。

    xxx_clear() 是 MySQL 8.4 所要求的。

  • xxx_add()

    此函数将针对属于同一个组的所有行调用。您应该使用它将 UDF_ARGS 参数中的值添加到您的内部汇总变量中。

    void xxx_add(UDF_INIT *initid, UDF_ARGS *args,
                 char *is_null, char *error);

聚合可加载函数的 xxx() 函数应该与非聚合可加载函数的声明方式相同。请参见 简单函数的可加载函数调用序列

对于聚合可加载函数,MySQL 会在处理完组中的所有行后调用 xxx() 函数。通常情况下,您不应该在这里访问其 UDF_ARGS 参数,而应该根据您的内部汇总变量返回一个值。

xxx() 中,返回值处理应该与非聚合可加载函数的返回值处理方式相同。请参见 可加载函数返回值和错误处理

xxx_reset()xxx_add() 函数处理其 UDF_ARGS 参数的方式与非聚合 UDF 的函数相同。请参见 可加载函数参数处理

指向 is_nullerror 的指针参数对于对 xxx_reset()xxx_clear()xxx_add()xxx() 的所有调用都是相同的。您可以使用它来记住是否发生了错误,或者 xxx() 函数是否应该返回 NULL。您不应将字符串存储到 *error 中! error 指向一个单字节变量,而不是字符串缓冲区。

*is_null 会针对每个组重置(在调用 xxx_clear() 之前)。*error 从不重置。

如果 *is_null*errorxxx() 返回时被设置,MySQL 会返回 NULL 作为组函数的结果。

可加载函数参数处理

args 参数指向一个 UDF_ARGS 结构,该结构具有此处列出的成员。

  • 无符号整数 arg_count

    参数数量。如果您的函数需要以特定数量的参数调用,请在初始化函数中检查此值。例如

    if (args->arg_count != 2)
    {
        strcpy(message,"XXX() requires two arguments");
        return 1;
    }

    对于其他值为数组的 UDF_ARGS 成员值,数组引用是从零开始的。也就是说,使用从 0 到 args->arg_count − 1 的索引值来引用数组成员。

  • 枚举 Item_result *arg_type

    指向包含每个参数类型的数组的指针。可能的类型值为 STRING_RESULTINT_RESULTREAL_RESULTDECIMAL_RESULT

    为了确保参数具有给定的类型,并在参数类型不匹配时返回错误,请在初始化函数中检查 arg_type 数组。例如

    if (args->arg_type[0] != STRING_RESULT ||
        args->arg_type[1] != INT_RESULT)
    {
        strcpy(message,"XXX() requires a string and an integer");
        return 1;
    }

    类型为 DECIMAL_RESULT 的参数以字符串形式传递,因此您应该以与处理 STRING_RESULT 值相同的方式来处理它们。

    作为要求函数参数具有特定类型的替代方法,您可以使用初始化函数将 arg_type 元素设置为所需的类型。这会导致 MySQL 在每次调用 xxx() 时将参数强制转换为这些类型。例如,要指定前两个参数应该分别强制转换为字符串和整数,请在 xxx_init() 中执行此操作。

    args->arg_type[0] = STRING_RESULT;
    args->arg_type[1] = INT_RESULT;

    精确值小数参数(如 1.3DECIMAL 列值)以 DECIMAL_RESULT 类型的形式传递。但是,这些值是以字符串形式传递的。要接收数字,请使用初始化函数指定参数应该强制转换为 REAL_RESULT

    args->arg_type[2] = REAL_RESULT;
  • 字符 **args

    args->args 将有关传递给函数的参数的通用性质的信息传递给初始化函数。对于常量参数 iargs->args[i] 指向参数值。(有关如何正确访问该值的说明,请参见后面部分。)对于非常量参数,args->args[i]0。常量参数是只使用常量的表达式,例如 34*7-2SIN(3.14)。非常量参数是指可能在行之间发生变化的值的表达式,例如列名或使用非常量参数调用的函数。

    对于主函数的每次调用,args->args 都包含当前正在处理的行传递的实际参数。

    如果参数 i 代表 NULLargs->args[i] 是一个空指针(0)。如果参数不是 NULL,函数可以按以下方式引用它

    • 类型为 STRING_RESULT 的参数以字符串指针加长度的形式给出,以支持处理二进制数据或任意长度的数据。字符串内容可以在 args->args[i] 中获取,字符串长度为 args->lengths[i]。不要假设字符串以 null 结尾。

      有关字符串参数的更多信息,请参见 可加载函数字符集处理

    • 对于类型为 INT_RESULT 的参数,您必须将 args->args[i] 强制转换为 long long

      long long int_val;
      int_val = *((long long*) args->args[i]);
    • 对于类型为 REAL_RESULT 的参数,您必须将 args->args[i] 强制转换为 double

      double    real_val;
      real_val = *((double*) args->args[i]);
    • 对于类型为 DECIMAL_RESULT 的参数,该值以字符串形式传递,应像处理 STRING_RESULT 值一样进行处理。

    • ROW_RESULT 参数未实现。

  • 无符号长整型 *lengths

    对于初始化函数,lengths 数组表示每个参数的最大字符串长度。您不应该更改这些。对于主函数的每次调用,lengths 都包含当前正在处理的行传递的任何字符串参数的实际长度。对于类型为 INT_RESULTREAL_RESULT 的参数,lengths 仍然包含参数的最大长度(与初始化函数相同)。

  • 字符 *maybe_null

    对于初始化函数,maybe_null 数组表示每个参数的值是否可能为 null(0 表示否,1 表示是)。

  • 字符 **attributes

    args->attributes 传递有关函数参数名称的信息。对于参数 i,属性名称可以在 args->attributes[i] 中以字符串形式获取,属性长度为 args->attribute_lengths[i]。不要假设字符串以 null 结尾。

    默认情况下,函数参数的名称是用于指定参数的表达式的文本。对于可加载函数,参数也可以有一个可选的 [AS] alias_name 子句,在这种情况下,参数名称为 alias_name。因此,每个参数的 attributes 值取决于是否给出了别名。

    假设一个可加载函数 my_udf() 被调用如下

    SELECT my_udf(expr1, expr2 AS alias1, expr3 alias2);

    在这种情况下,attributesattribute_lengths 数组将具有以下值

    args->attributes[0] = "expr1"
    args->attribute_lengths[0] = 5
    
    args->attributes[1] = "alias1"
    args->attribute_lengths[1] = 6
    
    args->attributes[2] = "alias2"
    args->attribute_lengths[2] = 6
  • 无符号长整型 *attribute_lengths

    attribute_lengths 数组指示每个参数名称的长度。

可加载函数返回值和错误处理

如果未发生错误,初始化函数应返回 0,否则返回 1。如果发生错误,xxx_init() 应将一个以 null 结尾的错误消息存储在 message 参数中。该消息将返回给客户端。消息缓冲区长 MYSQL_ERRMSG_SIZE 个字符。尝试将消息保持在 80 个字符以下,以便它适合标准终端屏幕的宽度。

主函数 xxx() 的返回值是函数值,对于 long longdouble 函数。字符串函数应返回指向结果的指针,并将 *length 设置为返回值的长度(以字节为单位)。例如

memcpy(result, "result string", 13);
*length = 13;

MySQL 使用 result 参数将缓冲区传递给 xxx() 函数。此缓冲区足够长,可以容纳 255 个字符,这些字符可以是多字节字符。如果结果适合,则 xxx() 函数可以将结果存储在此缓冲区中,在这种情况下,返回值应是指向缓冲区的指针。如果函数将结果存储在另一个缓冲区中,则它应返回指向该缓冲区的指针。

如果您的字符串函数未使用提供的缓冲区(例如,如果它需要返回一个长度超过 255 个字符的字符串),您必须使用 malloc()xxx_init() 函数或 xxx() 函数中为自己的缓冲区分配空间,并在 xxx_deinit() 函数中释放它。您可以在 UDF_INIT 结构中的 ptr 插槽中存储分配的内存,以供以后的 xxx() 调用重复使用。参见 简单函数的可加载函数调用序列

有关字符串参数的更多信息,请参见 可加载函数字符集处理

要指示主函数中返回值为 NULL,请将 *is_null 设置为 1

*is_null = 1;

要指示主函数中的错误返回值,请将 *error 设置为 1

*error = 1;

如果 xxx() 对任何行将 *error 设置为 1,则函数值为该行的当前行和在其中调用 XXX() 的语句处理的任何后续行的 NULL。 (xxx() 甚至不会被调用以处理后续行。)

可加载函数字符集处理

默认情况下,可加载函数不考虑字符串参数或返回值的字符集或排序规则。实际上,字符串参数和返回值被视为二进制字符串,这意味着只有包含单字节字符的字符串参数才能可靠地处理。

在 MySQL 8.4 中,编写可加载函数的接口使可加载函数能够确定字符串参数的字符集和排序规则,并返回具有特定字符集和排序规则的字符串。这些功能对于可加载函数编写者是可选的,他们可以根据需要利用它们。

在与 MySQL 一起分发的可加载函数中,与以下功能和扩展相关的那些利用了这些字符集功能:MySQL Enterprise Audit、MySQL Enterprise Firewall、MySQL Enterprise Data Masking and De-Identification、MySQL Keyring(仅适用于通用 Keyring 可加载函数,不适用于特定于特定 Keyring 插件的那些)和 Group Replication。这仅适用于有意义的情况。例如,返回加密数据的可加载函数旨在返回二进制字符串,而不是字符字符串。

可加载函数的字符集功能是使用 mysql_udf_metadata 服务器组件服务实现的。有关此服务的更多信息,请参见 MySQL Server Doxygen 文档,可在 https://dev.mysqlserver.cn/doc/index-other.html(搜索 s_mysql_mysql_udf_metadataudf_metadata_imp)获得。MySQL Keyring 可加载函数的源代码在社区源代码分发版中可用,可以作为希望修改其自己的可加载函数以支持字符集的第三方可加载函数编写者的示例。

如果可加载函数接受字符串参数或返回字符串值并被修改为支持字符集,则以下兼容性注意事项适用

  • 关于它们传递给可加载函数的参数,应用程序将继续工作,因为该函数现在能够处理任何字符集中的字符串参数,包括二进制字符串。

  • 如果可加载函数要以与其参数的字符集不同的字符集返回字符串结果,则该函数必须在内部执行字符集转换。例如,如果函数接受 latin1 参数,但返回 utf8mb4 结果,就会出现这种情况。

可加载函数编译和安装

实现可加载函数的文件必须在服务器运行的宿主机上编译和安装。此处介绍了在 MySQL 源代码分发版中包含的示例可加载函数文件 sql/udf_example.cc 的过程。有关可加载函数安装的更多信息,请参见 安装和卸载可加载函数

如果可加载函数将在将被复制到副本的语句中被引用,您必须确保每个副本也都有该函数可用。否则,当副本尝试调用该函数时,复制将失败。

udf_example.cc 文件包含以下函数

  • metaphon() 返回字符串参数的元音字符串。这有点像 soundex 字符串,但它更适合英语。

  • myfunc_double() 返回其参数中字符的 ASCII 值之和,除以其参数的长度之和。

  • myfunc_int() 返回其参数的长度之和。

  • sequence([const int]) 返回从给定数字或 1(如果未给出数字)开始的序列。

  • lookup() 返回主机名的 IP 地址。

  • reverse_lookup() 返回 IP 地址的主机名。该函数可以使用 'xxx.xxx.xxx.xxx' 形式的单个字符串参数调用,也可以使用四个数字调用。

  • avgcost() 返回平均成本。这是一个聚合函数。

在 Unix 和类 Unix 系统上,使用以下过程编译可加载函数

动态加载文件应编译为共享库文件,使用类似以下命令:

gcc -shared -o udf_example.so udf_example.cc

如果您使用的是 gccCMake(这就是 MySQL 本身的方式配置),您应该能够使用更简单的命令创建 udf_example.so

make udf_example

编译包含可加载函数的共享对象后,您必须安装它并告诉 MySQL 关于它。直接使用 gccudf_example.cc 编译共享对象会生成一个名为 udf_example.so 的文件。将共享对象复制到服务器的插件目录并将其命名为 udf_example.so。此目录由 plugin_dir 系统变量的值给出。

在某些系统上,ldconfig 程序(用于配置动态链接器)除非共享对象的名称以 lib 开头,否则不会识别该共享对象。在这种情况下,您应该将像 udf_example.so 这样的文件重命名为 libudf_example.so

在 Windows 上,使用以下过程编译可加载函数

  1. 获取 MySQL 源代码分发版。参见 如何获取 MySQL

  2. 如果需要,从 http://www.cmake.org 获取 CMake 构建工具。(需要 2.6 或更高版本)。

  3. 在源代码树中,在 sql 目录中查找名为 udf_example.defudf_example.cc 的文件。将这两个文件从该目录复制到您的工作目录。

  4. 创建一个 CMake makefile (CMakeLists.txt),其内容如下

    PROJECT(udf_example)
    
    # Path for MySQL include directory
    INCLUDE_DIRECTORIES("c:/mysql/include")
    
    ADD_DEFINITIONS("-DHAVE_DLOPEN")
    ADD_LIBRARY(udf_example MODULE udf_example.cc udf_example.def)
    TARGET_LINK_LIBRARIES(udf_example wsock32)
  5. 创建 VC 项目和解决方案文件,并替换适当的 generator

    cmake -G "generator"

    调用 cmake --help 会显示一个有效的生成器列表。

  6. 创建 udf_example.dll

    devenv udf_example.sln /build Release

在所有平台上,将共享库文件复制到 plugin_dir 目录后,使用以下语句通知 mysqld 新函数。文件名后缀因平台而异(例如,Unix 和类 Unix 系统上的 .so,Windows 上的 .dll),因此根据需要调整您的平台上的 .so 后缀。

CREATE FUNCTION metaphon RETURNS STRING
  SONAME 'udf_example.so';
CREATE FUNCTION myfunc_double RETURNS REAL
  SONAME 'udf_example.so';
CREATE FUNCTION myfunc_int RETURNS INTEGER
  SONAME 'udf_example.so';
CREATE FUNCTION sequence RETURNS INTEGER
  SONAME 'udf_example.so';
CREATE FUNCTION lookup RETURNS STRING
  SONAME 'udf_example.so';
CREATE FUNCTION reverse_lookup RETURNS STRING
  SONAME 'udf_example.so';
CREATE AGGREGATE FUNCTION avgcost RETURNS REAL
  SONAME 'udf_example.so';

安装后,函数会一直保持安装状态,直到卸载。

要删除函数,请使用 DROP FUNCTION

DROP FUNCTION metaphon;
DROP FUNCTION myfunc_double;
DROP FUNCTION myfunc_int;
DROP FUNCTION sequence;
DROP FUNCTION lookup;
DROP FUNCTION reverse_lookup;
DROP FUNCTION avgcost;

CREATE FUNCTIONDROP FUNCTION 语句会更新 mysql.func 系统表,该表充当可加载函数注册表。这些语句分别需要 INSERTDELETE 权限,用于 mysql 数据库。

在正常的启动序列期间,服务器将加载在 mysql.func 表中注册的函数。如果服务器使用 --skip-grant-tables 选项启动,则表中注册的函数不会加载,因此不可用。

可加载函数安全注意事项

MySQL 采取了多项措施来防止滥用可加载函数。

可加载函数库文件不能放在任意目录中。它们必须位于服务器的插件目录中。此目录由 plugin_dir 系统变量的值给出。

要使用 CREATE FUNCTIONDROP FUNCTION,您必须分别具有 INSERTDELETE 权限,用于 mysql 数据库。这是必要的,因为这些语句会添加和删除 mysql.func 表中的行。

可加载函数除了对应主 xxx() 函数的 xxx 符号之外,还应该至少定义一个符号。这些辅助符号对应于 xxx_init()xxx_deinit()xxx_reset()xxx_clear()xxx_add() 函数。 mysqld 还支持一个 --allow-suspicious-udfs 选项,用于控制是否可以加载仅具有 xxx 符号的可加载函数。默认情况下,该选项处于禁用状态,以防止尝试加载来自共享库文件的函数,而不是包含合法可加载函数的共享库文件。如果您有仅包含 xxx 符号的旧版可加载函数,并且无法重新编译以包含辅助符号,则可能需要指定 --allow-suspicious-udfs 选项。否则,应避免启用它。