分类: Mysql/postgreSQL
2008-05-12 15:18:35
本节介绍创建集合UDF之时需要定义的不同函数。27.2.3节,“添加新的自定义函数” 介绍了MySQL调用这些函数的顺序。
xxx_reset()
当MySQL在一个新组中发现第一行时调用这个函数。它对这个组重置任何内部总和变量,然后使用给定的UDF_ARGS参量作为内部总和值的第一个值。如下说明 xxx_reset() 函数:
char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
在MySQL5.1版中UDF接口不需要或不使用xxx_reset()函数,而是使用xxx_clear()函数作为替代。但是如果你想让UDF也能在老版本的服务器上运行,你也可以定义 xxx_reset() 和 xxx_clear() 函数。(如果你使用了这两个函数,xxx_reset()函数在很多情况下可以通过调用函数来内部实现,即调用xxx_clear()函数重置所有变量,然后添加UDF_ARGS参量作为组的第一个值。)
xxx_clear()
当MySQL需要重置总和结果时调用此函数。对每一个新组,在开始之时调用它,但是它也可以被调用来为一个没有匹配行在其中的查询重置值。如下说明xxx_clear():
char *xxx_clear(UDF_INIT *initid, char *is_null, char *error);
在调用xxx_clear()之前is_null 被设置指向 CHAR(0) 。
如果发生错误,你可以存储一个值在 error参量指向的变量中。error指向一单字节变量,而不是一个字符串缓冲区。
xxx_clear() 是MySQL 5.1必须的。
xxx_add()
为同组除了第一行之外,所有的行调用这个函数。你应该用它在UDF_ARGS参量中向内部总和变量加值。.
char *xxx_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
对集合UDF而言xxx() 函数应该用与非集合UDF一样的方法来说明。请参阅27.2.3.1节,“UDF调用简单函数的顺序”。
对一个集合UDF,MySQL 在组内所有行被处理之后调用xxx()函数。这里你应该一般不会接触到它的UDF_ARGS参量,但是取而代之地根据内部总和变量返回给你值。
在xxx()中处理的返回值应该用与对非集合UDF一样的方法来操作。请参阅27.2.3.4节,“UDF返回值和错误处理”。
xxx_reset() 和 xxx_add() 函数用与非集合UDF一样的方法来处理它们的UDF_ARGS 参量。请参阅27.2.3.3节,“UDF参量处理”。
到is_null和error的指针 参量和所有到xxx_reset(), xxx_clear(), xxx_add() 和 xxx()调用一样。你可以用这个来提醒你获取一个错误或无论xxx()是否返回NULL的一个结果。你不能把一个字符串存到*error!error指向单字节变量而不是字符串缓冲区。
*is_null 对每一个组都重置(调用xxx_clear()前), *error 从不重置。
如果 xxx()返回时,*is_null 或 *error 被设置,MySQL返回 NULL作为组函数的结果。
args 参数指向列着结构元的 UDF_ARGS 结构:
unsigned int arg_count
参量个数。如果你需要你的函数带着某个数目的参量被调用,在初始化函数检查这个值,例如:
if (args->arg_count != 2) { strcpy(message,"XXX() requires two arguments"); return 1; }
enum Item_result *arg_type
一个指针,对每个参量指向包含类型的一个数列。可能的类型值是STRING_RESULT, INT_RESULT 和 REAL_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; }
要求你函数的参量是某一类型的另一方法是,使用初始化函数设置arg_type元素为你想要的类型。对所有对xxx()的调用而言,这会导致MySQL强制参量为这些类型。比如,要指定投两个参量强制成字符串和整数,在xxx_init()中分别:
args->arg_type[0] = STRING_RESULT; args->arg_type[1] = INT_RESULT;
char **args
args->args 与初始化函数做有关传到你函数的参量的一般情况做通讯。对于常参量i,args->args[i] 指向参量值。(看下面的说明了解如何妥善地访问这个值)。对非-常参量,args->args[i] 为 0。一个常参量为仅使用参量的表达式,如 3 或 4*7-2 或 SIN(3.14)。一个非常 参量是一个行与行不同的表达式,如,列名或带非-常参量调用的函数。
对主函数的每次调用,args->args 包含为每个当前处理的行传递的实际参量。
如下使用参量i的函数:
给一个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 real_val; real_val = *((double*) args->args[i]);
unsigned long *lengths
对初始化函数,lengths数列表示对每个参量的最大字符串长度。你不要改变它。对主函数的每次调用,lengths包含了对当前处理行传递的任何字符串 参量的实际长度。对于INT_RESULT 或 REAL_RESULT类型的参量,lengths 仍包含参量的最大长度(对初始化函数)。
如果没有错误发生,初始化函数应该返回0,否则就返回1。如果有错误发生,xxx_init() 应该在message 参数存储一个以null结尾的错误消息。该消息被返回给客户端。消息缓冲区是 MYSQL_ERRMSG_SIZE 字符长度,但你应该试着把消息保持在少于80个字符,以便它能适合标准终端屏幕的宽度。
对于long long 和 double 类型的函数,主函数 xxx()的返回返回值是函数值。字符函数返回一个指向结果的指针,并且设置 *result 和 *length 为返回值的内容和长度。例如:
memcpy(result, "result string", 13); *length = 13;
被传给 xxx() 函数的结果缓冲区是 255 字节长。如果你的结果适合这个长度,你就不需要担心对结果的内存分配。
如果字符串函数需要返回一个超过255字节的字符串,你必须用 malloc() 在你的 xxx_init() 函数或者 xxx() 函数里为字符串分配空间,并且在 xxx_deinit() 函数里释放此空间。你可以将已分配内存存储在 UDF_INIT 结构里的 ptr 位置以备将来 xxx() 调用。请参阅27.2.3.1节,“UDF 对简单函数的调用顺序”。
要在主函数中指明一个 NULL 的返回值,设置 *is_null 为 1 :
*is_null = 1;
要在主函数中指明错误返回,设置 *error 为 1 :
*error = 1;
如果 xxx() 对任意行设置 *error 为 1 ,对于任何 XXX()被调用的语句处理的当前行和随后的任意行,该函数值为 NULL (甚至都不为随后的行调用 xxx())。
实现UDF的文件必须在运行服务器的主机上编译和安装。这个步骤在下面介绍,以包含在MySQL源码分发版里的UDF文件sql/udf_example.cc 为例。
紧接着下面的指令是对Unix的,对Windows的指令在本节稍后给出。
udf_example.cc 文件包含下列函数:
metaphon() 返回字符串参量的一个变音位(metaphon)字符串,这有点象一个探测法(soundex)字符串,但是它英语更协调。
myfunc_double()返回在其参量中所有字符的ASCII值的和,除以其 参量长度之和。
myfunc_int()返回其参量长度之和。
sequence([const int]) 返回一个序列,从给定数开始,若没有给定数则从1开始。
lookup() 返回对应主机名的IP数。
reverse_lookup() 返回对应一个IP数的主机名。函数可以带'xxx.xxx.xxx.xxx'形式的一个单字符串 参量调用,要么带4个数字调用。
一个可动态加载的文件应使用如下这样的命令编译为一个可共享的对象文件:
shell> gcc -shared -o udf_example.so udf_example.cc
如果你使用gcc,你应该能用一个更简单的命令创建udf_example.so :
shell> make udf_example.so
通过运行MySQL源码树下sql里的如下命令,你可以容易地为你的系统决定正确的编译器选项:
shell> make udf_example.o
你应该运行一个类似于make所显示那样的编译命令,除了要在行尾附近删除-c选项,并在行尾加上加上 -o udf_example.so。(在某些系统上,你可能需要在命令行留着-c 选项)。
编译好一个包含有UDF的共享目标后,你必须安装它并通知MySQL。从udf_example.cc编译一个共享目标文件产生一个名字类似于udf_example.so 的文件(确切名字可能因平台而异)。把这个文件复制到 /usr/lib 这样被你系统的动态(运行时)链接器搜索到的目录下,或者 把你放共享目标文件的目录添加到链接器配置文件(如,/etc/ld.so.conf )。
动态链接器的名字时系统特定的(如,在FreeBSD上是ld-elf.so.1 ,在Linux上是 ld.so,在Mac OS X上是dyld )。查看一下你系统的文档,看看链接器的名字是什么及如何配置链接器。
在许多系统上,你也可以设置环境变量LD_LIBRARY 或 LD_LIBRARY_PATH 指向你放UDF的目录。dlopen 手册会告诉你,在你系统上用哪个变量名。你可以在mysql.server 或 mysqld_safe 启动脚本里设置这个然后重启 mysqld。
在一些系统上,配置动态链接器的ldconfig不能识别不是以lib做名字开头的共享目标。在这种情况下,你应该把udf_example.so 改名为 libudf_example.so。
在Windows系统上,你可以通过下列步骤编译自定义函数:
你需要获得BitKeeper source repository for MySQL 5.1。 请参阅 2.8.3节,“从开发源树安装”。
在源数据仓里的VC++Files/examples/udf_example目录下,有名为udf_example.def, udf_example.dsp, 和 udf_example.dsw 的文件。
在数据仓的sql目录下,复制 udf_example.cc 文件到 VC++Files/examples/udf_example 目录,并改其名为udf_example.cpp。
Visual Studio VC++用打开 udf_example.dsw 文件,用它把UDF编译为一个一般项目。
共享目标文件安装完以后,为新函数信息修改 mysqld ,做如下声明:
mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME 'udf_example.so'; mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME 'udf_example.so'; mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME 'udf_example.so'; mysql> CREATE FUNCTION lookup RETURNS STRING SONAME 'udf_example.so'; mysql> CREATE FUNCTION reverse_lookup -> RETURNS STRING SONAME 'udf_example.so'; mysql> CREATE AGGREGATE FUNCTION avgcost -> RETURNS REAL SONAME 'udf_example.so';
可以使用DROP FUNCTION删除函数:
mysql> DROP FUNCTION metaphon; mysql> DROP FUNCTION myfunc_double; mysql> DROP FUNCTION myfunc_int; mysql> DROP FUNCTION lookup; mysql> DROP FUNCTION reverse_lookup; mysql> DROP FUNCTION avgcost;
CREATE FUNCTION 和 DROP FUNCTION 声明更新mysql 数据库中的func 系统表。函数名,类型和共享库名存进表中。你必须有mysql 数据库的INSERT 和DELETE 权限来创建和移除函数。
你不能使用 CREATE FUNCTION 去田间一个先前已经被创建的函数。如果你需要重新安装一个函数,你可以用DROP FUNCTION移除它,然后再用CREATE FUNCTION重新安装它。你可能会需要这么做,比如你重新编译新版本的函数以便mysqld得到这个新版本。不然,服务器还继续使用旧的版本。
一个有效程序是已被 CREATE FUNCTION加载且没有被DROP FUNCTION移除的函数。所有有效函数在每次服务器启动时重新加载,除非你使用--skip-grant-tables选项来启动mysqld。这种情况下,UDF的初始化将被跳过,UDF不可用。
MySQL 采取下列措施来防止误用自定义函数。
你必须有 INSERT 权限才能使用 CREATE FUNCTION 及有 DELETE 权限才能使用 DROP FUNCTION。这是很必要的,因为这些声明在mysql.func表里添加合删除行。
除了对应主 xxx()函数的xxx 符号,UDF应该至少定义一个符号。这些辅助符号对应 xxx_init(), xxx_deinit(), xxx_reset(), xxx_clear() 和 xxx_add() 函数。mysqld 也支持一个控制仅有一个xxx符号的UDF是否被加载的--allow-suspicious-udfs。这个选项 默认是关,以防止从共享目标文件而不是从这些已包含的合法UDF加载的企图。如果你有仅含xxx符号的老版本UDF,以及不能重编译来包含辅助符号的老版本UDF,那就有必要选--allow-suspicious-udfs 选项。否则,你应该避免打开这个选项。
UDF 目标文件不能放在任意目录。它们必须位于动态链接器被配置来搜索到的一些系统目录。为强制执行这个限制并防止指定被动态链接器搜索到的目录之外的路径,MySQL在加载函数的时候检查在CREATE FUNCTION 中指定的共享目标文件名,以及存在mysql.func表中的文件的路径分隔符。这防止通过直接操作mysql.func表指定非法路径名。有关UDF和运行时链接器,请参阅27.2.3.5节,“编译和安装自定义函数”。
下面介绍添加新固有函数的步骤。要注意你不能添加固有函数到二进制分发版里,因为这个步骤包含修改MySQL源代码。你必须从源码分发版自己编译MySQL。另外要注意,如果你把MySQL移植到另一个版本(比如新版本放出来的时候),你需要用新版本重复这个添加 步骤。
采取下列步骤来添加MySQL新的固有函数:
在定义函数名的lex.h文件中的sql_functions[]数列里添加一行。
如果函数原型是简单的(只有零个,一个,二个或三个参量),你应该在lex.h中指定 SYM(FUNC_ARGN) (其中N 是参量的个数)作为sql_functions[]数列中的第二个 参量,并添加一个在item_create.cc中创建函数目标的函数。可以看看 "ABS" 和 create_funcs_abs() 作为举例说明。
如果函数原型是复杂的(举例,如果函数有多种参量),你应该给sql_yacc.yy添加两行。一行表示yacc应该定义的预处理程序记号,(这应该在文件的开始添加)。然后定义函数 参数,并添加一个带这些参数的项到simple_expr分析规则中。举一个例子,你可以检查 sql_yacc.yy 中所有出现的ATAN 看看这个定义是什么样子的。
在 item_func.h中说明一个继承自Item_num_func 还是 Item_str_func的类,取决于你的函数是返回一个数还是一个字符串。
在 item_func.cc中是否添加下列说明之一,取决于你是定义一个数字函数还是字符函数:
double Item_func_newname::val() longlong Item_func_newname::val_int() String *Item_func_newname::Str(String *str)
如果你从任何标准项继承了你的目标(类似于Item_num_func),你或许只要定义这些函数中的一个,然后让父目标照管别的函数。比如,Item_str_func 类定义了一个 val() 函数,它这个函数对::str()返回的值进行 atof()操作。
你或许也定义了下列目标函数:
void Item_func_newname::fix_length_and_dec()
这个函数至少应该计算基于给定参量的max_length。 max_length 是函数可能返回字符的最大个数。如果主函数不能返回 NULL值,这个函数也应该设置 maybe_null = 0。函数可以通过检查函数的maybe_null值来检查是否有函数 参量能返回NULL值。你可以看一下Item_func_mod::fix_length_and_dec 作为典型的例子来说明这个问题。
所有函数都必须是线程安全的,换句话说就是,如果没有互斥体保护,不要在函数中使用任何全局或静态变量。
如果你想要从函数::val(), ::val_int()或::str()返回NULL,你应该设null_value为1,并返回0。
对于目标函数 ::str() 有一些需要而外考虑之处::
字符串参量*str 提供一个字符串缓冲可以用来保持结果(更多关于字符串类型的信息请参阅 sql_string.h文件)。
如果结果为NULL,::str() 函数应该返回保持这个结果的字符串或(char*) 0。
除非有绝对地需要,所有当前的字符串函数要避免分配内存!
在MySQL中,你可以用C++定义一个步骤,在一个查询被发送到客户端之前访问和修改其中的数据。修改可以一行接一行地做,或者按照级别成组(GROUP)地做。
我们创建一个范例步骤来演示你可以做的。
此外,我们推荐你看一下mylua。通过它你可以用 LUA语言把运行时里的一个 步骤加载到mysqld中。
analyse([max_elements,[max_memory]])
这个步骤在sql/sql_analyse.cc定义,这个步骤检查你查询的结果,并且返回对此结果的一个分析:
max_elements (默认值 256) 是analyse注意到每 列不同值的最高数目。analyse使用此 参数来检查是否最优化的列的类型是ENUM类型。
max_memory (默认值 8192) 是analyse在查找所有不同值时分配给每 列的最大内存数。i
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max_elements,[max_memory]])
当前来说,相关的文档只有源码。
检查下列文件可以获得关于步骤的所有信息:
sql/sql_analyse.cc
sql/procedure.h
sql/procedure.cc
sql/sql_select.cc
这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问dev.mysql.com。 原始参考手册为英文版,与英文版参考手册相比,本翻译版可能不是最新的。