MySQL自定义函数(User Define Function)开发实例——发送TCP/UDP消息
开发背景
监控数据库中某个字段的值,当它改为特定值时向通知其它系统以执行业务逻辑。
实现思路
监控数据库中特定字段值的变化可以用数据库触发器实现,在触发器中发送udp消息通知其它系统。
难点在于触发器中能执行的都是数据库定义好的方法,它们都无法实现这个需求。自定义函数(User Define Function)允许我们创建自己的函数,实现自己的逻辑,就像MySQL本来就有这个函数一样。
我们实现这样一个自定义的函数,接收一个字符串参数,然后将这个字符传通过udp发送到指定端口。
开发前准备
MySQL自定义函数仅支持C/C++开发,所以需要一些C/C++的基础。
这里以在Windows中使用Visual Studio2022开发进行介绍。
创建一个C++库项目,添加依赖项libmysql.lib、ws2_32.lib,引入头文件"mysql.h" <ws2tcpip.h>
自定义函数我这里取名为SendG,使用方式为SendG(string)
MySQL自定义函数设计说明
提前说明:本文只介绍满足前面需求下的自定义函数设计方法。
这里要涉及两个函数
bool SendG_init(UDF_INIT* init, UDF_ARGS* args, char* message) 这个后缀_init方法顾名思义是一个初始化函数
void SendG(UDF_INIT* init, UDF_ARGS* args, char* result, unsigned long* length, char* is_null, char* error) 是主体函数
当我们每次调用SendG(string) 时会先调用 _init后缀的函数,然后才会调用主体函数中的业务逻辑。因此我们可以在SendG_init函数中检查参数是否正确,在SendG函数中执行具体的业务逻辑。
函数的命名是固定的必须取名为 xxxx_init和xxxx
至于UDF_INIT UDF_ARGS等结构的使用方法则不用担心,MySQL的头文件中有详细介绍,基本一看就大概知道怎么用,例如以下是UDF_ARGS的定义:
本文会用到arg_count、arg_type、lengths这几个字段,通过他们的命名结合注释就可以知道具体含义,这里不在啰嗦。
具体实现代码
#include "pch.h"
#include "mysql.h"
#include <ws2tcpip.h>
extern "C" {
__declspec(dllexport)
bool SendG_init(UDF_INIT* tinit, UDF_ARGS* args, char* message) {
if (args->arg_count != 1) {
char ms[]{ "Only one parameter is accepted in the call to udf 'SendG'" };
memcpy_s(message, sizeof(ms), ms, sizeof(ms));
return true;
}
else if (args->arg_type[0] != STRING_RESULT) {
char ms[]{ "Only string parameters are accepted in the call to udf 'SendG'" };
memcpy_s(message, sizeof(ms), ms, sizeof(ms));
return true;
}
return false;
}
__declspec(dllexport)
void SendG(UDF_INIT* tinit, UDF_ARGS* args, char* result, unsigned long* length, char* is_null, char* error) {
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in RecvAddr{};
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(54321);
inet_pton(AF_INET, "127.0.0.1", &RecvAddr.sin_addr);
*is_null = 1;
sendto(sock, args->args[0], args->lengths[0], 0, (SOCKADDR*)&RecvAddr, sizeof(RecvAddr));
closesocket(sock);
}
}
我们在SendG_init中检查参数的数量和类型是否正确,SendG_init的返回true代表有错误,返回false才表示没问题(这里和WinAPI比较像,返回的HRESULT是0表示正常执行 )。参数错误时向message写入具体的错误提示,帮助调用者正确使用我们的函数。
然后会执行SendG主体函数,这里创建一个SOCKET使用UDP协议将传入的字符串发送给本机的54321端口,最后关闭SOCKET。
这里需要注意两点:
1、函数必须使用extern "C"导出C语言标准的函数
2、在这个例子中SendG方法是不需要有执行结果的,所以它的返回值类型是void,其次因为没有返回值所以这里必须使用*is_null=1允许方法返回NULL。如果有返回值则正常返回具体的类型,可选的返回值类型在Item_result中定义(init中检查参数类型时就用到了)它与UDF_ARGS在同一个头文件中,这里就不具体展开了。
自定义函数的注册与卸载
在使用前需要先部署注册我们的自定义方法,相当于告诉数据库有这个方法。
首先将生成的dll放在MySQL的plugin目录中,可使用select @@plugin_dir查询。然后使用CREATE FUNCTION SendG RETURNS string SONAME 'SUDP.dll' 向数据库注册函数SendG
现在执行select SendG('abcde')就会向54321端口发送abcde了(可以改写 SendG让它接受两个参数,把端口号作为第二个参数,这样SendG方法就更加灵活了)
卸载SendG使用DROP FUNCTION SendG;
注册和卸载都不需要重启MySQL服务。
总结
MySQL的UDF提供了开发自定义函数的功能,实际在这里可以执行我们自己写的任意代码(别说发UDP/TCP消息了就是发邮件都行)。
业务系统可以通过SQL给数据库系统传递信息,UDF技术则帮助我们实现了数据库系统向业务系统传递消息。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?