小巧、快速、可靠。
選擇其中三項。
執行時期可載入擴充功能

1. 概述

SQLite 能夠在執行時期載入擴充功能(包括新的應用程式定義的 SQL 函式排序序列虛擬表格虛擬檔案系統 (VFSes))。此功能允許擴充功能的程式碼與應用程式分開開發和測試,然後再依需求載入。

擴充功能也可以與應用程式靜態連結。以下顯示的程式碼範本,無論是作為靜態連結的擴充功能還是執行時期可載入的擴充功能,都能正常運作。但您應該為進入點函式 ("sqlite3_extension_init") 指定不同的名稱,以避免在應用程式包含兩個或多個擴充功能時發生名稱衝突。

2. 載入擴充功能

SQLite 擴充功能是一個共用程式庫或 DLL。要載入它,您需要提供 SQLite 包含共用程式庫或 DLL 的檔案名稱,以及初始化擴充功能的進入點。在 C 程式碼中,此資訊是使用 sqlite3_load_extension() API 提供的。有關該例程的更多資訊,請參閱其文件。

請注意,不同的作業系統對其共用程式庫使用不同的檔案名稱後綴。Windows 使用 ".dll",Mac 使用 ".dylib",而大多數非 Mac 的 Unix 系統則使用 ".so"。如果您希望程式碼具有可移植性,可以省略共用程式庫檔案名稱中的後綴,sqlite3_load_extension() 介面會自動新增適當的後綴。

還有一個 SQL 函式可用於載入擴充功能:load_extension(X,Y)。它的運作方式與 sqlite3_load_extension() C 介面相同。

載入擴充功能的兩種方法都允許您指定擴充功能進入點的名稱。您可以將此參數留空 — 為 sqlite3_load_extension() C 語言介面傳遞 NULL 指標,或省略 load_extension() SQL 介面的第二個參數 — 擴充功能載入器邏輯會自行嘗試找出進入點。它會先嘗試使用通用的擴充功能名稱 "sqlite3_extension_init"。如果這樣做無效,它會使用範本 "sqlite3_X_init" 建構一個進入點名稱,其中 X 會替換為檔案名稱中最後一個 "/" 之後和第一個 "." 之前的每個 ASCII 字元的對應小寫字母,如果前三個字元恰好是 "lib",則會省略它們。因此,例如,如果檔案名稱是 "/usr/lib/libmathfunc-4.8.so",則進入點名稱將是 "sqlite3_mathfunc_init"。或者,如果檔案名稱是 "./SpellFixExt.dll",則進入點將稱為 "sqlite3_spellfixext_init"。

基於安全考量,擴充功能載入預設為關閉。為了使用 C 語言或 SQL 擴充功能載入函式,必須先在應用程式中使用 sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,1,NULL) C 語言 API 啟用擴充功能載入。

命令列介面,可以使用 ".load" 點命令載入擴充功能。例如:

.load ./YourCode

請注意,命令列介面程式已經為您啟用了擴充功能載入(透過在其設定過程中呼叫 sqlite3_enable_load_extension() 介面),因此上述命令無需任何特殊開關、設定或其他複雜操作即可運作。

使用單一參數的 ".load" 指令會呼叫 sqlite3_load_extension(),並將 zProc 參數設為 NULL,這會導致 SQLite 首先尋找名為 "sqlite3_extension_init" 的進入點,然後尋找 "sqlite3_X_init",其中 "X" 衍生自檔案名稱。如果您的擴充功能的進入點名稱不同,只需將該名稱作為第二個參數提供即可。例如:

.load ./YourCode nonstandard_entry_point

3. 編譯可載入擴充功能

可載入擴充功能是 C 語言程式碼。在大多數類 Unix 作業系統上編譯它們,通常的指令如下:

gcc -g -fPIC -shared YourCode.c -o YourCode.so

Mac 電腦是類 Unix 系統,但它們不遵循通常的共享函式庫慣例。要在 Mac 上編譯共享函式庫,請使用如下指令:

gcc -g -fPIC -dynamiclib YourCode.c -o YourCode.dylib

如果您在嘗試載入函式庫時收到錯誤訊息「mach-o, but wrong architecture」(mach-o,但架構錯誤),則可能需要根據應用程式的建置方式,在 gcc 中新增指令列選項「-arch i386」或「arch x86_64」。

若要使用 MSVC 在 Windows 上編譯,通常可以使用類似以下的指令:

cl YourCode.c -link -dll -out:YourCode.dll

若要使用 MinGW 在 Windows 上編譯,指令列與 Unix 上的指令列相同,只是輸出檔案的副檔名改為「.dll」,並且省略 -fPIC 參數。

gcc -g -shared YourCode.c -o YourCode.dll

4. 編寫可載入擴充功能

一個可載入擴充功能的範本包含以下三個元素:

  1. 在原始程式碼檔案的頂部使用 "#include <sqlite3ext.h>" 取代 "#include <sqlite3.h>".

  2. "。將巨集 "SQLITE_EXTENSION_INIT1" 放在 "#include <sqlite3ext.h>" 行之後的單獨一行上。

  3. 新增一個擴充功能載入進入點常式,看起來類似以下內容:

    #ifdef _WIN32
    __declspec(dllexport)
    #endif
    int sqlite3_extension_init( /* <== Change this name, maybe */
      sqlite3 *db, 
      char **pzErrMsg, 
      const sqlite3_api_routines *pApi
    ){
      int rc = SQLITE_OK;
      SQLITE_EXTENSION_INIT2(pApi);
      /* insert code to initialize your extension here */
      return rc;
    }
    

    最好將進入點的名稱自訂為與您將產生的共享函式庫的名稱相對應,而不是使用通用的 "sqlite3_extension_init" 名稱。為擴充功能指定自訂進入點名稱,可以讓您將兩個或多個擴充功能靜態連結到同一個程式中,而不會發生連結器衝突,如果您稍後決定使用靜態連結而不是執行階段連結的話。如果您的共享函式庫最終被命名為如上述編譯器範例所示的「YourCode.so」、「YourCode.dll」或「YourCode.dylib」,則正確的進入點名稱應為「sqlite3_yourcode_init」。

以下是一個完整的範本擴充功能,您可以複製/貼上以開始使用:

/* Add your header comment here */
#include <sqlite3ext.h> /* Do not use <sqlite3.h>! */
SQLITE_EXTENSION_INIT1

/* Insert your extension code here */

#ifdef _WIN32
__declspec(dllexport)
#endif
/* TODO: Change the entry point name so that "extension" is replaced by
** text derived from the shared library filename as follows:  Copy every
** ASCII alphabetic character from the filename after the last "/" through
** the next following ".", converting each character to lowercase, and
** discarding the first three characters if they are "lib".
*/
int sqlite3_extension_init(
  sqlite3 *db,
  char **pzErrMsg,
  const sqlite3_api_routines *pApi
){
  int rc = SQLITE_OK;
  SQLITE_EXTENSION_INIT2(pApi);
  /* Insert here calls to
  **     sqlite3_create_function_v2(),
  **     sqlite3_create_collation_v2(),
  **     sqlite3_create_module_v2(), and/or
  **     sqlite3_vfs_register()
  ** to register the new features that your extension adds.
  */
  return rc;
}

4.1. 擴充功能範例

許多完整且可運作的可載入擴充功能範例可以在 SQLite 原始程式碼樹狀結構的 ext/misc 子目錄中看到。該目錄中的每個檔案都是一個獨立的擴充功能。檔案的標頭註釋提供了說明文件。以下簡要說明 ext/misc 子目錄中的一些擴充功能:

其他更複雜的擴充功能可以在 ext/ 以外的子資料夾中找到,而不是 ext/misc/。

5. 持續性可載入擴充功能

載入式擴充功能的預設行為是,當最初呼叫 sqlite3_load_extension() 的資料庫連線關閉時,它會從處理程序記憶體中卸載。(換句話說,當資料庫連線關閉時,所有擴充功能的 sqlite3_vfs 物件的 xDlClose 方法都會被呼叫。)但是,如果初始化程序返回 SQLITE_OK_LOAD_PERMANENTLY 而不是 SQLITE_OK,則擴充功能將不會被卸載(xDlClose 不會被呼叫),並且擴充功能將無限期地保留在處理程序記憶體中。SQLITE_OK_LOAD_PERMANENTLY 返回值對於想要註冊新的 VFS 的擴充功能很有用。

clarification: 初始化函數返回 SQLITE_OK_LOAD_PERMANENTLY 的擴充功能在資料庫連線關閉後仍會繼續存在於記憶體中。但是,擴充功能並*不會*自動向後續的資料庫連線註冊。這使得載入實作新的 VFS 的擴充功能成為可能。要永久載入並註冊實作新的 SQL 函數、排序序列和/或虛擬表格的擴充功能,以便這些新增的功能可供所有後續的資料庫連線使用,則初始化常式也應該在將註冊這些服務的子函數上呼叫 sqlite3_auto_extension()

vfsstat.c 擴充功能展示了一個永久註冊新的 VFS 和新的虛擬表格的載入式擴充功能的範例。該擴充功能中的 sqlite3_vfsstat_init() 初始化常式只會在擴充功能第一次載入時被呼叫一次。它只在那一次註冊新的「vfslog」VFS,並返回 SQLITE_OK_LOAD_PERMANENTLY,以便用於實作「vfslog」VFS 的程式碼將保留在記憶體中。初始化常式還會在指向「vstatRegister()」函數的指標上呼叫 sqlite3_auto_extension(),以便所有後續的資料庫連線在啟動時都會呼叫「vstatRegister()」函數,從而註冊「vfsstat」虛擬表格。

6. 靜態連結執行時期可載入擴充功能

完全相同的原始碼可以用於執行時期可載入的共用程式庫或 DLL,也可以用作與應用程式靜態連結的模組。這提供了靈活性,並允許您以不同的方式重複使用相同的程式碼。

要靜態連結您的擴充功能,只需新增 -DSQLITE_CORE 編譯時期選項。SQLITE_CORE 巨集會導致 SQLITE_EXTENSION_INIT1 和 SQLITE_EXTENSION_INIT2 巨集變成無運算。然後修改您的應用程式以直接呼叫進入點,並將 NULL 指標作為第三個「pApi」參數傳入。

如果您要靜態連結兩個或多個擴充功能,則使用基於擴充功能檔案名稱的進入點名稱(而不是泛型「sqlite3_extension_init」進入點名稱)尤其重要。如果您使用泛型名稱,則同一個符號將會有多個定義,並且連結將會失敗。

如果您將在應用程式中開啟多個資料庫連線,而不是分別為每個資料庫連線呼叫擴充功能進入點,則您可能需要考慮使用 sqlite3_auto_extension() 介面來註冊您的擴充功能,並使它們在每個資料庫連線開啟時自動啟動。您只需要註冊每個擴充功能一次,而且您可以在 main() 常式的開頭附近執行此操作。使用 sqlite3_auto_extension() 介面註冊您的擴充功能,會使您的擴充功能像內建於核心 SQLite 中一樣運作 - 它們會在您開啟新的資料庫連線時自動存在,而無需初始化。只要確保在註冊擴充功能之前完成您需要使用 sqlite3_config() 執行的任何設定即可,因為 sqlite3_auto_extension() 介面會隱含地呼叫 sqlite3_initialize()

7. 實作細節

SQLite 使用 sqlite3_vfs 物件的 xDlOpen()、xDlError()、xDlSym() 和 xDlClose() 方法來實作執行時期擴充套件載入。在 Unix 系統上,這些方法是使用 dlopen() 函式庫來實作的(這也解釋了為什麼 SQLite 在 Unix 系統上通常需要連結 "-ldl" 函式庫),而在 Windows 系統上則是使用 LoadLibrary() API。在針對特殊系統的客製化 VFS 中,可以省略所有這些方法,如此一來執行時期擴充套件載入機制將無法運作(但假設入口點名稱是唯一的,您仍然可以靜態連結擴充套件程式碼)。SQLite 可以透過 SQLITE_OMIT_LOAD_EXTENSION 編譯,從建置中省略擴充套件載入程式碼。

本頁最後修改時間:2024-01-31 23:45:50 UTC