延伸 MySQL 9.0  /  新增函數至 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++ 函數名稱在完成的函數中保持可讀。

可載入函數介面函數

以下清單描述您撰寫的 C++ 函數,以實作名為 XXX() 的函數介面。主要函數 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() 函數應如本節所示宣告。請注意,傳回類型和參數會因您宣告 SQL 函數 XXX() 為傳回 STRINGINTEGER,還是 REAL 而有所不同,這取決於 CREATE FUNCTION 陳述式中的宣告。

針對 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() 可能會傳回 NULLxxx_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 提供。

    在以下情況下,引數會使用 31 的 decimals 值:例如,FLOATDOUBLE 資料行宣告時沒有明確的小數位數(例如,FLOAT 而非 FLOAT(10,3)),以及浮點常數(例如 1345E-3)。它也用於字串和其他可能在函數中轉換為數值形式的非數字引數。

    decimals 成員初始化的值只是預設值。可以在函數內變更它以反映執行的實際計算。預設值的決定方式為使用引數的最大小數位數。如果甚至有一個引數的小數位數為 NOT_FIXED_DEC,則該值會用於 decimals

  • unsigned int max_length

    結果的最大長度。預設的 max_length 值會因函數的結果類型而異。對於字串函數,預設值是最長引數的長度。對於整數函數,預設值為 21 位數。對於實數函數,預設值為 13 加上 initid->decimals 指示的小數位數。(對於數值函數,長度會包含任何符號或小數點字元。)

    如果您想要傳回 blob 值,您可以將 max_length 設定為 65KB 或 16MB。此記憶體不會配置,但該值會用於決定是否需要暫時儲存資料時要使用哪種資料類型。

  • char *ptr

    函數可以用於自身用途的指標。例如,函數可以使用 initid->ptr 在彼此之間傳遞配置的記憶體。xxx_init() 應配置記憶體並將其指派給此指標

    initid->ptr = allocated_memory;

    xxx()xxx_deinit() 中,請參閱 initid->ptr 以使用或釋放記憶體。

  • bool const_item

    如果 xxx() 總是傳回相同的值,xxx_init() 應將 const_item 設定為 1,否則設為 0

彙總函數的可載入函數呼叫順序

本節說明在您建立彙總可載入函數時需要定義的不同介面函數。如需 MySQL 呼叫這些函數的順序資訊,請參閱可載入函數介面函數

  • xxx_reset()

    當 MySQL 在新的群組中找到第一列時,會呼叫此函數。它應重設任何內部摘要變數,然後使用給定的 UDF_ARGS 引數作為群組中內部摘要值的起始值。宣告 xxx_reset(),如下所示

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

    在 MySQL 9.0 中不需要或使用 xxx_reset(),在該版本中,可載入函數介面會改用 xxx_clear()。但是,如果您想要讓函數與舊版伺服器搭配使用,您可以定義 xxx_reset()xxx_clear()。(如果您同時包含這兩個函數,在許多情況下,可以透過呼叫 xxx_clear() 來重設所有變數,然後呼叫 xxx_add()UDF_ARGS 引數新增為群組中的第一個值,以在內部實作 xxx_reset() 函數。)

  • xxx_clear()

    當 MySQL 需要重設摘要結果時,會呼叫此函數。它會在每個新群組的開頭呼叫,但也可以呼叫它來重設沒有相符列的查詢值。宣告 xxx_clear(),如下所示

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

    在呼叫 xxx_clear() 之前,is_null 會設定為指向 CHAR(0)

    如果發生錯誤,您可以將值儲存在 error 引數指向的變數中。error 指向單一位元組變數,而不是字串緩衝區。

    MySQL 9.0 需要 xxx_clear()

  • 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 的函數相同。請參閱可載入函數引數處理

對於所有對 xxx_reset()xxx_clear()xxx_add()xxx() 的呼叫,is_nullerror 的指標引數都相同。您可以使用它來記住您收到錯誤,或者 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;
    }

    對於其他為陣列的 UDF_ARGS 成員值,陣列參照是以零為基礎的。也就是說,使用索引值 0 到 args->arg_count − 1 來參照陣列成員。

  • enum 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;
  • char **args

    args->args 會將關於傳遞給您函數之引數的一般性質的資訊傳達給初始化函數。對於常數引數 iargs->args[i] 指向引數值。(請參閱後續說明,了解如何正確存取值。)對於非常數引數,args->args[i]0。常數引數是僅使用常數的表示式,例如 34*7-2SIN(3.14)。非常數引數是指向可能會逐列變更之值的表示式,例如欄名稱或使用非常數引數呼叫的函數。

    對於主要函數的每個調用,args->args 都包含目前正在處理的列所傳遞的實際引數。

    如果引數 i 代表 NULL,則 args->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 引數未實作。

  • unsigned long *lengths

    對於初始化函數,lengths 陣列會指出每個引數的最大字串長度。您不應變更這些。對於主要函數的每個調用,lengths 都會包含目前正在處理的列所傳遞之任何字串引數的實際長度。對於類型為 INT_RESULTREAL_RESULT 的引數,lengths 仍然包含引數的最大長度 (如同初始化函數一樣)。

  • char *maybe_null

    對於初始化函數,maybe_null 陣列會指出每個引數的引數值是否可能為 null (如果否則為 0,如果是則為 1)。

  • char **attributes

    args->attributes 會傳達關於函數引數名稱的資訊。對於引數 i,屬性名稱會以字串形式在 args->attributes[i] 中提供,而屬性長度則為 args->attribute_lengths[i]。請勿假設字串以 null 結尾。

    預設情況下,函數引數的名稱是指定引數所用之表示式的文字。對於可載入函數,引數也可能具有選用的 [AS] 別名 子句,在這種情況下,引數名稱為 別名。因此,每個引數的 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
  • unsigned long *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 9.0 中,用於撰寫可載入函數的介面可讓可載入函數判斷字串引數的字元集和定序,並傳回具有特定字元集和定序的字串。這些功能對於可載入函數撰寫者而言是選用的,他們可以根據需要利用這些功能。

在 MySQL 隨附散佈的可載入函數中,與以下功能和擴充功能相關聯的那些函數會利用這些字元集功能:MySQL Enterprise Audit、MySQL Enterprise Firewall、MySQL Enterprise Data Masking and De-Identification、MySQL Keyring (只有通用金鑰環可載入函數,不包括特定於特定金鑰環外掛程式的那些函數) 和 Group Replication。這僅適用於有意義的地方。例如,傳回加密資料的可載入函數是預期傳回二進位字串,而不是字元字串。

可載入函數的字元集功能是使用 mysql_udf_metadata 伺服器元件服務實作的。有關此服務的資訊,請參閱 MySQL Server Doxygen 文件,網址為 https://mysqldev.dev.org.tw/doc/index-other.html (搜尋 s_mysql_mysql_udf_metadataudf_metadata_imp)。MySQL Keyring 可載入函數的原始程式碼可在社群來源散佈中取得,第三方可載入函數撰寫者可以將其作為範例來檢查,以修改他們自己的可載入函數以支援字元集。

如果可載入函數採用字串引數或傳回字串值,並且修改為支援字元集,則以下相容性考量適用

  • 關於它們傳遞給可載入函數的引數,應用程式將繼續運作,因為該函數現在能夠處理任何字元集中的字串引數,包括二進位字串。

  • 如果要讓可載入函數以與其引數字元集不同的字元集傳回字串結果,則該函數必須在內部執行字元集轉換。例如,如果函數接受 latin1 引數但傳回 utf8mb4 結果,就會發生這種情況。

可載入函數的編譯和安裝

實作可載入函數的檔案必須在執行伺服器的主機上編譯和安裝。此處描述了包含在 MySQL 來源散佈中的範例可載入函數檔案 sql/udf_example.cc 的程序。有關可載入函數安裝的其他資訊,請參閱安裝和解除安裝可載入函數

如果將在將複寫到複本的陳述式中參考可載入函數,則您必須確保每個複本也都有可用的函數。否則,當複本嘗試調用該函數時,複寫會在複本上失敗。

udf_example.cc 檔案包含以下函數

  • metaphon() 會傳回字串引數的 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。使用 gcc 直接從 udf_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 makefileCMakeLists.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 系統資料表,該表作為可載入函數的註冊表。這些語句需要 mysql 資料庫的 INSERTDELETE 權限。

在正常的啟動順序中,伺服器會載入 mysql.func 表中註冊的函數。如果伺服器使用 --skip-grant-tables 選項啟動,則不會載入表中註冊的函數,且這些函數將不可用。

可載入函數安全性預防措施

MySQL 採取多項措施來防止濫用可載入函數。

可載入函數程式庫檔案不能放置在任意目錄中。它們必須位於伺服器的外掛程式目錄中。此目錄由 plugin_dir 系統變數的值給定。

若要使用 CREATE FUNCTIONDROP FUNCTION,您必須分別具有 mysql 資料庫的 INSERTDELETE 權限。這是必要的,因為這些語句會從 mysql.func 表中新增和刪除列。

可載入函數除了對應於主 xxx() 函數的 xxx 符號之外,至少應定義一個符號。這些輔助符號對應於 xxx_init()xxx_deinit()xxx_reset()xxx_clear()xxx_add() 函數。mysqld 也支援 --allow-suspicious-udfs 選項,該選項控制是否可以載入只有 xxx 符號的可載入函數。預設情況下,該選項處於禁用狀態,以防止嘗試從除包含合法可載入函數之外的共享程式庫檔案中載入函數。如果您有僅包含 xxx 符號且無法重新編譯以包含輔助符號的舊可載入函數,則可能需要指定 --allow-suspicious-udfs 選項。否則,您應該避免啟用它。