本文中的「限制」是指不可超過的大小或數量。我們關心的是諸如 BLOB 中的位元組最大數量或資料表中的欄位最大數量等事項。
SQLite 最初的設計政策是避免任何限制。當然,在記憶體和磁碟空間有限的機器上執行的每個程式都有某種限制。但在 SQLite 中,這些限制並未定義明確。政策是,如果它能放入記憶體中,而且可以用 32 位元整數計算,那麼它應該可以正常運作。
不幸的是,無限制政策已被證明會產生問題。由於上限未定義明確,因此未進行測試,而且在將 SQLite 推向極限時,經常會發現錯誤。因此,自 3.5.8 版(2008-04-16)左右的 SQLite 版本都有定義明確的限制,而且這些限制會作為 測試套件 的一部分進行測試。
本文定義了 SQLite 的限制為何,以及如何為特定應用程式自訂這些限制。限制的預設設定通常相當大,足以應付幾乎所有應用程式。有些應用程式可能想要在此處或彼處增加限制,但我們預期這種需求很少見。更常見的是,應用程式可能想要重新編譯 SQLite,設定更低的限制,以避免在較高層級的 SQL 陳述式產生器中發生錯誤時浪費過多資源,或協助阻止注入惡意 SQL 陳述式的攻擊者。
可以在執行階段使用 sqlite3_limit() 介面,針對每個連線變更某些限制,並使用其中一個 限制類別,這些類別是為該介面定義的。執行階段限制是為具有多個資料庫的應用程式所設計,其中一些資料庫僅供內部使用,而其他資料庫則可能受到潛在敵對外部代理的影響或控制。例如,網頁瀏覽器應用程式可能使用內部資料庫來追蹤歷史頁面檢視,但會有一個或多個獨立資料庫,由從網際網路下載的 JavaScript 應用程式建立和控制。 sqlite3_limit() 介面允許由可信賴程式碼管理的內部資料庫不受限制,同時對由不可信賴外部程式碼建立或控制的資料庫施加嚴格限制,以協助防止阻斷服務攻擊。
字串或 BLOB 的最大長度
SQLite 中字串或 BLOB 的最大位元組數是由預處理器巨集 SQLITE_MAX_LENGTH 定義的。此巨集的預設值為 10 億(10 億或 1,000,000,000)。您可以使用類似下列命令列選項在編譯階段提高或降低此值
-DSQLITE_MAX_LENGTH=123456789
目前的實作僅支援長度達 231-1 或 2147483647 的字串或 BLOB。某些內建函數(例如 hex())可能在達到該點之前就失敗。在安全性敏感的應用程式中,最好不要嘗試增加最大字串和 blob 長度。事實上,您最好將最大字串和 blob 長度降低到數百萬左右的範圍(如果可能的話)。
在 SQLite 的 INSERT 和 SELECT 處理過程中,資料庫中每列的完整內容會編碼為單一 BLOB。因此,SQLITE_MAX_LENGTH 參數也決定了列中的最大位元組數。
可以在執行階段使用 sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size) 介面降低最大字串或 BLOB 長度。
最大欄位數
SQLITE_MAX_COLUMN 編譯時期參數用於設定上限,包括:
SQLITE_MAX_COLUMN 的預設設定為 2000。您可以在編譯時期將其變更為最大 32767 的值。另一方面,許多經驗豐富的資料庫設計師會主張,一個正規化的資料庫永遠不需要資料表中超過 100 個欄位。
在大部分應用程式中,欄位數很小,只有數十個。SQLite 程式碼產生器中有些地方會使用 O(N²) 演算法,其中 N 是欄位數。因此,如果您將 SQLITE_MAX_COLUMN 重新定義為一個非常大的數字,而且您產生使用大量欄位的 SQL,您可能會發現 sqlite3_prepare_v2() 執行速度很慢。
使用 sqlite3_limit(db,SQLITE_LIMIT_COLUMN,size) 介面可以在執行時降低最大欄位數。
SQL 陳述式的最大長度
SQL 陳述式文字中的最大位元組數限制為 SQLITE_MAX_SQL_LENGTH,預設為 1,000,000,000。
如果 SQL 陳述式限制為一百萬位元組長度,那麼顯然您將無法透過將多百萬位元組字串嵌入在 INSERT 陳述式中作為文字值來插入。但您不應該這樣做。為您的資料使用主機 參數。準備簡短的 SQL 陳述式,如下所示
INSERT INTO tab1 VALUES(?,?,?);
然後使用 sqlite3_bind_XXXX() 函式將您的大型字串值繫結到 SQL 陳述式。繫結的使用消除了轉譯字串中引號字元的需要,降低了 SQL 注入攻擊的風險。由於不必解析或複製大型字串,因此執行速度也更快。
使用 sqlite3_limit(db,SQLITE_LIMIT_SQL_LENGTH,size) 介面可以在執行時降低 SQL 陳述式的最大長度。
聯結中表格的最大數目
SQLite 不支援包含超過 64 個表格的聯結。此限制源於 SQLite 程式碼產生器在查詢最佳化器中使用每聯結表格一個位元的位元圖。
SQLite 使用高效能的 查詢規劃器演算法,因此即使是大型聯結也可以快速地 準備。因此,沒有機制可以提高或降低聯結中表格數量的限制。
表達式樹的最大深度
SQLite 會將表達式剖析為樹狀結構以進行處理。在產生程式碼期間,SQLite 會遞迴地遍歷這個樹狀結構。因此,表達式樹狀結構的深度受到限制,以避免使用過多的堆疊空間。
SQLITE_MAX_EXPR_DEPTH 參數會決定表達式樹狀結構的最大深度。如果值為 0,則不強制執行任何限制。目前的實作預設值為 1000。
如果 SQLITE_MAX_EXPR_DEPTH 最初為正值,則可以在執行階段使用 sqlite3_limit(db,SQLITE_LIMIT_EXPR_DEPTH,size) 介面降低表達式樹狀結構的最大深度。換句話說,如果表達式深度已經有編譯階段的限制,則可以在執行階段降低最大表達式深度。如果 SQLITE_MAX_EXPR_DEPTH 在編譯階段設定為 0(如果表達式深度沒有限制),則 sqlite3_limit(db,SQLITE_LIMIT_EXPR_DEPTH,size) 會是空操作。
函式上參數的最大數量
SQLITE_MAX_FUNCTION_ARG 參數會決定傳遞給 SQL 函式的參數最大數量。此限制的預設值為 100。SQLite 應該可以使用有數千個參數的函式。然而,我們懷疑任何嘗試呼叫有超過幾個參數的函式的人,實際上是想在使用 SQLite 的系統中尋找安全漏洞,而不是做有用的工作,因此我們將這個參數設定得相對較低。
傳遞給函式的參數數量有時會儲存在有號字元中。因此,SQLITE_MAX_FUNCTION_ARG 的上限為 127。
可以在執行階段使用 sqlite3_limit(db,SQLITE_LIMIT_FUNCTION_ARG,size) 介面降低函式中的參數最大數量。
複合 SELECT 陳述式中項目的最大數量
複合 SELECT 陳述式是由兩個或多個 SELECT 陳述式透過運算子 UNION、UNION ALL、EXCEPT 或 INTERSECT 連接而成。我們將複合 SELECT 中的每個個別 SELECT 陳述式稱為「項目」。
SQLite 中的程式碼產生器會使用遞迴演算法處理複合 SELECT 陳述式。為了限制堆疊大小,因此我們限制複合 SELECT 中的詞彙數。詞彙的最大數目是 SQLITE_MAX_COMPOUND_SELECT,預設為 500。我們認為這是個寬大的配置,因為在實務上我們幾乎從未看過複合選擇中的詞彙數超過個位數。
複合 SELECT 詞彙的最大數目可以在執行時間使用 sqlite3_limit(db,SQLITE_LIMIT_COMPOUND_SELECT,size) 介面降低。
LIKE 或 GLOB 模式的最大長度
SQLite 預設 LIKE 和 GLOB 實作中使用的模式比對演算法在特定病態案例中會展現 O(N²) 效能(其中 N 是模式中字元的數目)。為了避免惡意人士能夠指定自己的 LIKE 或 GLOB 模式而發動阻斷服務攻擊,LIKE 或 GLOB 模式的長度限制在 SQLITE_MAX_LIKE_PATTERN_LENGTH 位元組。此限制的預設值為 50000。現代工作站甚至可以相當快速地評估 50000 位元組的病態 LIKE 或 GLOB 模式。阻斷服務問題只會在模式長度達到數百萬位元組時發生。儘管如此,由於大多數有用的 LIKE 或 GLOB 模式長度最多只有數十位元組,偏執的應用程式開發人員可能會希望將此參數降低到數百的範圍內,如果他們知道外部使用者能夠產生任意模式的話。
LIKE 或 GLOB 模式的最大長度可以在執行時間使用 sqlite3_limit(db,SQLITE_LIMIT_LIKE_PATTERN_LENGTH,size) 介面降低。
單一 SQL 陳述式中主機參數的最大數目
主機 參數 是 SQL 陳述式中的佔位符,使用其中一個 sqlite3_bind_XXXX() 介面填入。許多 SQL 程式設計師熟悉使用問號 ("?") 作為主機參數。SQLite 也支援以冒號 (":")、美元符號 ("$") 或 "@" 為前綴的名稱主機參數,以及 "?123" 形式的編號主機參數。
SQLite 陳述式中的每個主機參數都會指定一個數字。這些數字通常從 1 開始,並隨著每個新參數增加一。但是,當使用 "?123" 形式時,主機參數數字就是問號後面的數字。
SQLite 會分配空間來儲存 1 到所使用的最大主機參數數字之間的所有主機參數。因此,包含像 ?1000000000 這樣主機參數的 SQL 陳述式會需要數 GB 的儲存空間。這可能會輕易耗盡主機電腦的資源。為了防止過度記憶體配置,主機參數數字的最大值是 SQLITE_MAX_VARIABLE_NUMBER,它在 3.32.0 (2020-05-22) 之前的 SQLite 版本預設為 999,在 3.32.0 之後的 SQLite 版本預設為 32766。
可以在執行階段使用 sqlite3_limit(db,SQLITE_LIMIT_VARIABLE_NUMBER,size) 介面降低最大主機參數數字。
觸發遞迴的最大深度
SQLite 會限制觸發遞迴的深度,以防止涉及遞迴觸發的陳述式使用無限量的記憶體。
在 SQLite 版本 3.6.18(2009-09-11)之前,觸發器不是遞迴的,因此這個限制沒有意義。從版本 3.6.18 開始,支援遞迴觸發器,但必須使用 PRAGMA recursive_triggers 陳述式明確啟用。從 版本 3.7.0(2009-09-11)開始,遞迴觸發器預設啟用,但可以使用 PRAGMA recursive_triggers 手動停用。只有在啟用遞迴觸發器時,SQLITE_MAX_TRIGGER_DEPTH 才具有意義。
預設最大觸發器遞迴深度為 1000。
附加資料庫的最大數量
ATTACH 陳述式是 SQLite 擴充功能,允許兩個或多個資料庫與同一個資料庫連線關聯,並運作得好像它們是一個單一資料庫。同時附加的資料庫數量限制為 SQLITE_MAX_ATTACHED,預設設定為 10。附加資料庫的最大數量無法增加到超過 125。
可以在執行階段使用 sqlite3_limit(db,SQLITE_LIMIT_ATTACHED,size) 介面降低附加資料庫的最大數量。
資料庫檔案中的最大頁數
SQLite 能夠限制資料庫檔案的大小,以防止資料庫檔案過大並消耗過多磁碟空間。SQLITE_MAX_PAGE_COUNT 參數是單一資料庫檔案中允許的最大頁數。嘗試插入會導致資料庫檔案大於此值的新資料,將會傳回 SQLITE_FULL。
SQLITE_MAX_PAGE_COUNT 最大可能的設定值為 4294967294 (232-2)。從版本 3.45.0(2024-01-15)開始,4294967294 也是 SQLITE_MAX_PAGE_COUNT 的預設值。與預設頁面大小 4096 位元組一起使用時,這會產生大約 17.5 太位元組的最大資料庫大小。如果頁面大小增加到最大值 65536 位元組,資料庫檔案可以成長到大約 281 太位元組。
可以在執行階段使用 max_page_count PRAGMA 來提升或降低此限制。
資料表中列的最大數目
資料表中列的理論最大數目為 264 (18446744073709551616 或約為 1.8e+19)。由於資料庫最大容量為 281 太位元組,因此無法達到此限制。281 太位元組的資料庫最多只能容納約 2e+13 列,而且僅在沒有索引且每列包含極少資料的情況下才能達到。
資料庫最大容量
每個資料庫都包含一個或多個「頁面」。在單一資料庫中,每個頁面大小相同,但不同的資料庫可以擁有 512 到 65536(含)之間的 2 的次方頁面大小。資料庫檔案的最大容量為 4294967294 頁。在 65536 位元組的最大頁面大小下,這轉換為約 1.4e+14 位元組(281 太位元組,或 256 太位元組,或 281474 吉位元組或 256,000 吉位元組)的最大資料庫容量。
此特定上限尚未測試,因為開發人員無法取得可達到此限制的硬體。不過,測試確實驗證了當資料庫達到底層檔案系統的最大檔案大小(通常遠小於理論最大資料庫大小)以及當資料庫無法因磁碟空間耗盡而成長時,SQLite 會正確且正常地運作。
架構中資料表的最大數目
每個資料表和索引都需要資料庫檔案中至少一個頁面。前一句中的「索引」是指使用 CREATE INDEX 陳述式明確建立的索引或由 UNIQUE 和 PRIMARY KEY 約束建立的隱式索引。由於資料庫檔案中頁面的最大數目為 2147483646(略大於 20 億),因此這也是架構中資料表和索引數目的上限。
每當資料庫開啟時,整個架構會被掃描和解析,而架構的解析樹會保存在記憶體中。這表示資料庫連線啟動時間和初始記憶體使用量與架構的大小成正比。