SQLite 架構
介紹
本文描述了SQLite庫的架構。本文資訊對哪些想要理解或修改SQLite內部工作內容的人非常有用。
附圖展示了SQLite的主要元件以及他們是如何相互操作的。其下的文字解釋了各個元件的角色。
總覽
SQLite 編譯SQL文字為位元組碼 bytecode , 然後使用虛擬機器執行位元組碼來工作。
sqlite3_prepare_v2() 以及相關介面扮演著編譯器的角色,將SQL文字轉換成位元組碼。 sqlite3_stmt 物件是一個容器。該容器用於包含一個實現某一SQL的位元組碼程式。 sqlite3_step() 介面向虛擬機器傳遞位元組碼程式, 並執行位元組碼程式直到完成,或返回產生的一行結果,或碰到一個致命fatal錯誤, 或被打斷。
介面
大部分的C語言的介面在 main.c , legacy.c 和 vdbeapi.c 這些原始檔中,儘管一些程式被分散在其他檔案中,他們可以通過檔案域訪問資料結構。 sqlite3_get_table() 程式在 table.c 中實現. sqlite3_mprintf() 程式在 printf.c 中實現。 sqlite3_complete() 介面在 tokenize.c 中. TCL Interface 在 tclsqlite.c 中實現.
為了避免命名衝突,SQLite庫的所有的外部符號都使用 sqlite3 字首。那些用於外部使用的(換句話說,那些符號形成了SQLite的API)符號增加下劃線,因此以 sqlite3_ 開頭。 擴充套件API有時候在下劃線前增加副檔名;例如: sqlite3rbu_ 或 sqlite3session_ 。
分詞器
當一個包含SQL句子的字串被處理時,它首先會被髮送到分詞器。分詞器將SQL文字拆分成詞,然後將詞一個接一個傳遞給解析器。分詞器程式碼在 tokenize.c 檔案中。 注意在這個設計中,分詞器呼叫解析器。熟悉YACC和BISON的人可能習慣用相反的方式-讓解析器呼叫分詞器。讓分詞器呼叫解析器的方式更好一些,因為這樣能夠執行緒安全,並且執行的更快。
解析器
解析器根據上下文為詞賦予意思。SQLite的解析器使用 Lemon parser generator 生成。Lemon和YACC/BISON做同樣的事情,但是它使用另一種不容易出錯的輸入語法。Lemon生成一個可重入的,執行緒安全的parser。Lemon定義了一個非終端析構的概念,因此在遇到語法錯誤時它不會記憶體洩漏。在 parse.y 中可以找到驅動Lemon的語法檔案和SQLite理解SQL語言的定義的檔案。 因為Lemon這個程式一般不會在開發機器上找到,所以Lemon完整的原始碼(就一個C檔案)被包含在SQLite分發的“tool”子目錄中。
程式碼生成器
在解析器將詞裝配成解析樹後,程式碼生成器開始分析解析樹,生成執行SQL語句工作的位元組碼。 prepared statement 物件是包含了這些位元組碼的容器。 程式碼生成器有很多檔案,包含: attach.c , auth.c , build.c , delete.c , expr.c , insert.c , pragma.c , select.c , trigger.c , update.c , vacuum.c , where.c , wherecode.c , 和 whereexpr.c . 大部分重要的魔法發生在這些檔案中。 expr.c 操縱著程式碼生成器的表達。 where .c * 操縱程式碼生成器處理SELECT,UPDATE和DELETE的WHERE從句。 attach.c , delete.c , insert.c , select.c , trigger.c update.c , and vacuum.c 這幾個檔案操縱程式碼生成器處理和檔名同名的SQL語句。(所有這些檔案必要時在 expr.c 和 where.c 中呼叫)所有其他SQL語句被編寫在 build.c . auth.c 檔案實現了 sqlite3_set_authorizer() 的功能。
程式碼生成器,尤其時在 where*.c 和 select.c 中的邏輯,有時候被稱為查詢計劃 query planner 。對於任何具體的SQL語句,可能有成千上百個不同的演算法來計算結果。查詢計劃query planner時一個力求從這麼多演算法中選擇一個最好的演算法的人工智慧。
位元組碼引擎
位元組碼程式被程式碼生成器生成,執行在虛擬機器上。 虛擬機器自身完整地包含在單獨的 vdbe.c 原始檔中。 vdbe.h 標頭檔案在虛擬機器和剩餘的SQLite庫之間定義了介面, vdbeInt.h 定義的結構和介面是虛擬機器自己私有的。其他幾個 vdbe*.c 檔案是虛擬機器的助手。 vdbeaux.c 檔案包含虛擬機器使用的有效工具和剩餘SQLite庫用於構建VM程式的介面模組。 vdbeapi.c 檔案包含虛擬機器的擴充套件介面,如 sqlite3_bind_int() 和 sqlite3_step() 。獨立的值 (strings, integer, floating point numbers, and BLOBs) 被儲存在由 vdbemem.c 實現,命名為“Mem”的內部物件中。
SQLite使用C語言程式的callbacks來實現SQL函式。即使內建的SQL函式也是這個方式實現。大部分內建SQL函式(如: abs() , count() , substr() , 等等) 能夠在 func.c 原始檔中找到。日期和時間轉換函式在 date.c 中找到。 一些函式如 coalesce() 和 typeof() 由程式碼生成器直接生成位元組碼實現。
B-Tree
SQLite資料庫使用B-tree來儲存資料到磁碟,B-tree在原始檔 btree.c 中實現。在資料庫中的每個表和索引使用單獨的B-tree。所有的B-tree儲存在同一個磁碟檔案中。檔案格式 file format 細節是穩定的、明確的、在更新中保證相容性的。
B-tree子系統和其他SQLite庫的互動介面在標頭檔案 btree.h 中定義。
頁快取
B-tree模組以固定大小的頁向磁碟請求資訊。預設的頁大小是4096位元組,也可以是512位元組和65525位元組之間任意的2的n次冪。頁快取負責讀、寫、快取這些頁。頁快取同時提供回滾和原子提交的抽象,同時負責鎖定資料庫檔案。B-tree驅動從頁快取請求指定的頁,當它想修改頁、或提交、或回滾修改時,通知頁快取。頁快取確保快速、安全、高效地處理請求的所有混雜細節。 基本的頁快取在 pager.c 檔案中實現。 WAL mode 邏輯在單獨的 wal.c 中。記憶體快取在 pcache.c 和 pcache1.c 檔案中實現。在 pager.h 標頭檔案中,實現了頁快取子系統和SQLite庫的其他系統互動的介面。
作業系統介面
SQLite使用抽象的 VFS 物件,提供在不同作業系統間的移植能力。每個VFS提供了開啟(opening)、讀(read)、寫(writing)和關閉(closing)磁碟上的檔案的方法; 提供具體的作業系統任務,比如查詢當前時間,獲取隨機數用來初始化內建的偽隨機數生成器。SQLite當前版本為unix在 os_unix.c 檔案提供VFS,為windows在 os_win.c 檔案提供VFS。
工具
記憶體分配程式,不區分大小寫的字串比較程式,可移植的文字轉數字程式,以及其他工具在 util.c 檔案中。在解析器使用的符號表使用 hash.c 中的hash表來儲存。 utf.c 原始檔包含了Unicode轉換子程式。SQLite在 printf.c 有自己私有的 printf() 實現,包含了一些擴充套件。SQLite在 random.c 中有自己的偽隨機數生成器(PRNG)
測試程式碼
在"src/"資料夾中,那些以 test 開頭的原始檔是用來做測試的,不會被包含在構建的庫中。