MySQL UDF (User-Defined Function) 允許以 C/C++ 撰寫自訂函數,擴展 SQL 能力。Windows 平台需編譯為 .dll 動態連結庫。本文以 Visual Studio (VC++) 為例,完整示範建立、編譯、部署、註冊與測試流程。
include 目錄與 lib 目錄。sql/udf_example.cc)。my_udf。C:\Program Files\MySQL\MySQL Server 8.0\includeHAVE_DLOPEN=1;MYSQL_DYNAMIC_PLUGIN=1my_udf.def (下一步建立)。多執行緒 (/MT) 或 多執行緒 DLL (/MD),與 MySQL 編譯選項一致 (通常 /MD)。#include
#include
#include
#include // String 類別
extern "C" {
// 函數宣告
bool my_name_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
void my_name_deinit(UDF_INIT *initid);
char *my_name(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);
}
// 初始化:檢查參數、分配記憶體
bool my_name_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
if (args->arg_count != 1) {
strcpy(message, "my_name() requires exactly one argument");
return true; // 回傳 true 表示錯誤
}
if (args->arg_type[0] != STRING_RESULT) {
strcpy(message, "my_name() argument must be a string");
return true;
}
// 可分配私有記憶體 initid->ptr = malloc(...)
initid->maybe_null = 0; // 回傳非 NULL
initid->const_item = 0; // 非常數
return false; // 成功
}
// 釋放資源
void my_name_deinit(UDF_INIT *initid) {
// if (initid->ptr) free(initid->ptr);
}
// 主運算函數
char *my_name(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) {
char *input = args->args[0];
unsigned long input_len = args->lengths[0];
// 簡單範例:回傳 "Hello, <輸入>!"
const char *prefix = "Hello, ";
const char *suffix = "!";
unsigned long need = strlen(prefix) + input_len + strlen(suffix) + 1;
if (need > 255) { // UDF 字串回傳上限 255 字節 (舊版限制),新版可更大
*error = 1;
*is_null = 1;
return 0;
}
char *pos = result;
memcpy(pos, prefix, strlen(prefix)); pos += strlen(prefix);
memcpy(pos, input, input_len); pos += input_len;
memcpy(pos, suffix, strlen(suffix)); pos += strlen(suffix);
*pos = '\0';
*length = (unsigned long)(pos - result);
*is_null = 0;
*error = 0;
return result;
}
LIBRARY my_udf
EXPORTS
my_name_init
my_name_deinit
my_name
確保匯出函數名稱與來源碼完全一致 (大小寫敏感)。
x64/Release/my_udf.dll。my_udf.dll 複製到 MySQL plugin_dir 目錄:SHOW VARIABLES LIKE 'plugin_dir'; → 通常為 C:\Program Files\MySQL\MySQL Server 8.0\lib\plugin\。C:\Windows\System32),但強烈建議放在 plugin_dir,便於管理與安全。mysql -u root -p
CREATE FUNCTION my_name RETURNS STRING SONAME 'my_udf.dll';
-- 成功顯示 Query OK, 0 rows affected
RETURNS STRING:回傳類型 (STRING/INTEGER/REAL/DECIMAL)。SONAME:DLL 檔名 (不含路徑),MySQL 會在 plugin_dir 尋找。SELECT my_name('World');
-- 預期輸出:Hello, World!
SELECT my_name(column1) FROM test_table;
DROP FUNCTION my_name;
-- 重新編譯 DLL 覆蓋後,需重啟 MySQL 或執行 FLUSH FUNCTIONS; (8.0.4+ 支援)
-- 8.0.4 前必須重啟 mysqld 才能重新載入 DLL
需實作 xxx_init, xxx_deinit, xxx_add, xxx_clear, xxx 五個函數,CREATE AGGREGATE FUNCTION 註冊。參考官方文件 sql/udf_example.cc 中的 sum_udf 範例。
C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld.exe,命令引數 --console --defaults-file="C:\ProgramData\MySQL\MySQL Server 8.0\my.ini",直接附加偵錯。my_printf_error 或寫入檔案輸出除錯訊息。.err 檔) 中的 UDF 載入訊息。| 錯誤 | 原因 | 解決 |
|---|---|---|
| Can't open shared library | DLL 路徑錯誤、架構不符 (32/64位) | 確認 plugin_dir、編譯 x64、DLL 置於正確目錄 |
| Symbol not found | .def 匯出名稱與來源碼不符、C++ 名稱修飾 | 使用 extern "C"、.def 精確列出函數名 |
| Function registration failed | 初始化函數回傳 true (錯誤) | 檢查 init 函數邏輯、參數驗證、message 緩衝區大小 |
| Crash on call | 記憶體存取違規、緩衝區溢位 | 嚴格檢查 args->lengths、result 緩衝區大小、使用 String 類別 |
Windows 上開發 MySQL UDF 關鍵在於:正確的 VS 專案設定 (include、定義、.def、x64/Release)、extern "C" 防止名稱修飾、plugin_dir 部署、CREATE FUNCTION 註冊。掌握此流程,即可為 MySQL 注入高效能、領域特定的計算邏輯。