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技術則幫助我們實現了數據庫系統向業務系統傳遞消息。
浙公網安備 33010602011771號