SQLite 資料庫極度不易損毀。如果應用程式崩潰、作業系統崩潰,甚至在交易過程中發生斷電,部分寫入的交易應在下次存取資料庫檔案時自動回滾。復原過程完全自動化,不需要使用者或應用程式採取任何行動。
儘管 SQLite 不易損毀,但並非免疫。本文件說明 SQLite 資料庫可能損毀的各種方式。
SQLite 資料庫檔案是普通的磁碟檔案。這表示任何程序都可以開啟檔案並以垃圾內容覆寫檔案。SQLite 函式庫無法防禦此種情況。
我們已見過多個案例,其中檔案描述符在檔案上開啟,然後該檔案描述符關閉並在 SQLite 資料庫上重新開啟。稍後,其他一些執行緒繼續寫入舊檔案描述符,而沒有意識到原始檔案已關閉。但由於檔案描述符已由 SQLite 重新開啟,因此原本要寫入原始檔案的資訊最終覆寫了 SQLite 資料庫的部分內容,導致資料庫損毀。
此問題的一個範例發生在 2013-08-30 左右,在 Fossil DVCS 的正規儲存庫上。在那個事件中,檔案描述符 2(標準錯誤)在 sqlite3_open_v2() 之前被錯誤地關閉(我們懷疑是 stunnel 所為),因此用於儲存庫資料庫檔案的檔案描述符為 2。之後,一個應用程式錯誤導致一個 assert() 陳述式透過呼叫 write(2,...) 來發出錯誤訊息。但由於檔案描述符 2 現在已連接到一個資料庫檔案,因此錯誤訊息會覆寫資料庫的一部分。為了防止此類問題,SQLite 3.8.1 版(2013-10-17)及後續版本拒絕對資料庫檔案使用編號較低的檔案描述符。(請參閱 SQLITE_MINIMUM_FILE_DESCRIPTOR 以取得更多資訊。)
另一個由使用已關閉的檔案描述符所造成的損毀範例是 由 Facebook 工程師在 2014-08-12 的部落格文章中所報告 的。
另一個此錯誤的範例是在 2019-07-11 對 Fossil 所報告的。一個檔案描述符會開啟用於除錯輸出,但之後會被 SQLite 關閉並重新開啟。但除錯邏輯會繼續寫入原始檔案描述符。請參閱 論壇討論 以取得錯誤報告和修復連結。
在背景中執行自動備份的系統可能會嘗試在交易期間備份 SQLite 資料庫檔案。備份副本可能會包含一些舊內容和一些新內容,因此會損毀。
建立 SQLite 資料庫可靠備份副本的最佳方法是使用 SQLite 函式庫中所包含的 備份 API。如果無法使用,只要沒有任何程序正在進行交易,複製 SQLite 資料庫檔案是安全的。如果先前的交易失敗,則必須將任何回滾日誌(*-journal 檔案)或寫入前記錄(*-wal 檔案)與資料庫檔案本身一起複製。
SQLite 通常將所有內容儲存在單一磁碟檔案中。但是,在執行交易時,在崩潰或斷電後復原資料庫所需的資訊會儲存在輔助日誌檔案中。此類日誌檔案會被描述為 "熱"。日誌檔案的名稱與原始資料庫檔案相同,但會加上 -journal 或 -wal 字尾。
SQLite 必須看到日誌檔案才能從崩潰或斷電中復原。如果 熱日誌檔案 在崩潰或斷電後被移動、刪除或重新命名,則自動復原將無法運作,而且資料庫可能會損毀。
此問題的另一個表現是 由不一致使用 8+3 檔名所造成的資料庫損毀。
前一個範例是一個更一般問題的具體案例:SQLite 資料庫的狀態是由資料庫檔案和日誌檔案共同控制的。在靜止狀態下,日誌檔案不存在,只有資料庫檔案有關係。但是,如果日誌檔案存在,則必須與資料庫一起保留,以避免損毀。下列動作都可能導致損毀
SQLite 在資料庫檔案和 寫入前記錄 或 WAL 檔案上使用檔案鎖定,以協調並發程序之間的存取。如果沒有協調,兩個執行緒或程序可能會同時嘗試對資料庫檔案進行不相容的變更,導致資料庫損毀。
SQLite 依賴基礎檔案系統執行鎖定,就像文件所述。但有些檔案系統的鎖定邏輯包含錯誤,因此鎖定並不總是像宣傳的那樣運作。這在網路檔案系統和 NFS 中尤其明顯。如果在鎖定原語包含錯誤的檔案系統上使用 SQLite,而且兩個或多個執行緒或程序同時嘗試存取同一個資料庫,則可能會導致資料庫損毀。
SQLite 在 Unix 平台上使用的預設鎖定機制是 POSIX 建議鎖定。不幸的是,POSIX 建議鎖定有設計缺陷,容易被誤用和失敗。特別是,在同一個程序中,任何具有持有 POSIX 建議鎖定的檔案描述子的執行緒都可以使用不同的檔案描述子覆寫該鎖定。一個特別惡劣的問題是,close() 系統呼叫會取消程序中所有執行緒和所有檔案描述子對同一個檔案的所有 POSIX 建議鎖定。
因此,例如,假設多執行緒程序有兩個或更多執行緒,它們與同一個資料庫檔案有獨立的 SQLite 資料庫連線。然後,第三個執行緒出現,並且想要從同一個資料庫檔案讀取某些內容,而不使用 SQLite 函式庫。第三個執行緒執行 open()、read(),然後執行 close()。人們會認為這應該是無害的。但是,close() 系統呼叫會導致所有其他執行緒在資料庫上持有的鎖定被中斷。那些其他執行緒無法知道他們的鎖定剛剛被破壞(POSIX 沒有提供任何機制來確定這一點),因此他們會在他們的鎖定仍然有效的假設下繼續執行。這可能會導致兩個或更多執行緒或程序同時嘗試寫入資料庫,從而導致資料庫損毀。
請注意,兩個或更多執行緒使用 SQLite 函式庫存取同一個 SQLite 資料庫檔案是完全安全的。SQLite 的 unix 驅動程式知道 POSIX 建議性鎖定的怪癖,並且會解決這些怪癖。這個問題只會在執行緒嘗試繞過 SQLite 函式庫並直接讀取資料庫檔案時發生。
正如前一段所指出的,SQLite 會採取措施來解決 POSIX 建議性鎖定的怪癖。解決方案的一部分包括保留一個開啟的 SQLite 資料庫檔案的全球清單(受互斥鎖保護)。但是,如果多個 SQLite 副本連結到同一個應用程式,那麼這個全球清單就會有多個執行個體。使用一個 SQLite 函式庫副本開啟的資料庫連線將不知道使用另一個副本開啟的資料庫連線,並且無法解決 POSIX 建議性鎖定的怪癖。一個連線上的 close() 操作可能會不知不覺地清除另一個資料庫連線上的鎖定,從而導致資料庫損毀。
上述場景聽起來很牽強。但 SQLite 開發人員知道至少有一個商業產品發布時帶有這個錯誤。供應商來找 SQLite 開發人員尋求協助,追蹤他們在 Linux 和 Mac 上看到的一些不頻繁的資料庫損毀問題。最後追蹤到問題出在應用程式連結到兩個獨立的 SQLite 副本。解決方案是變更應用程式的建置程序,只連結到一個 SQLite 副本,而不是兩個。
SQLite 在 unix 平台上使用的預設鎖定機制是 POSIX 建議鎖定,但還有其他選項。透過使用 sqlite3_open_v2() 介面選擇替代的 sqlite3_vfs,應用程式可以使用其他可能更適合某些檔案系統的鎖定協定。例如,點檔案鎖定可能會選擇用於必須在不支援 POSIX 建議鎖定的 NFS 檔案系統上執行的應用程式。
所有連線到同一個資料庫檔案都使用相同的鎖定協定非常重要。如果一個應用程式使用 POSIX 建議鎖定,而另一個應用程式使用點檔案鎖定,那麼這兩個應用程式將不會看到彼此的鎖定,也無法協調資料庫存取,可能會導致資料庫損毀。
如果兩個程序對同一個資料庫檔案開啟連線,然後其中一個程序關閉其連線、取消連結檔案,然後在同一個位置建立一個新的資料庫檔案,並使用同一個名稱重新開啟新的檔案,則這兩個程序將會對同一個名稱的不同資料庫檔案進行對話。(請注意,這僅在允許在檔案仍開啟進行讀寫時取消連結檔案的 Posix 和類似的 Posix 系統上才有可能。Windows 不允許發生這種情況。)由於回滾記錄檔和 WAL 檔案是基於資料庫檔案的名稱,因此這兩個不同的資料庫檔案將共用同一個回滾記錄檔或 WAL 檔案。其中一個資料庫的回滾或復原可能會使用另一個資料庫的內容,導致損毀。如果在開啟資料庫檔案時將其重新命名,並建立一個具有舊名稱的新檔案,也會發生類似的問題。
換句話說,取消連結或重新命名開啟的資料庫檔案會導致行為未定義,而且可能不受歡迎。
從 SQLite 版本 3.7.17(2013-05-20)開始,如果資料庫檔案在仍使用時取消連結,unix 作業系統介面會將 SQLITE_WARNING 訊息傳送至 錯誤記錄檔。
如果單一資料庫檔案具有多個連結(硬連結或軟連結),則這只是另一種表示該檔案具有多個名稱的方式。如果兩個或多個程序使用不同的名稱開啟資料庫,則它們將使用不同的回滾記錄檔和 WAL 檔案。這表示如果其中一個程序崩潰,另一個程序將無法復原進行中的交易,因為它會在錯誤的地方尋找適當的記錄檔。
換句話說,開啟和使用具有兩個或多個名稱的資料庫檔案會導致行為未定義,而且可能不受歡迎。
從 SQLite 版本 3.7.17(2013-05-20)開始,如果資料庫檔案有多個硬連結,unix 作業系統介面會將 SQLITE_WARNING 訊息傳送至 錯誤記錄。
從 SQLite 版本 3.10.0(2016-01-06)開始,unix 作業系統介面會嘗試解析符號連結並以其正規名稱開啟資料庫檔案。在版本 3.10.0 之前,透過符號連結開啟資料庫檔案類似於開啟具有多個硬連結的資料庫檔案,並導致未定義的行為。
不要開啟 SQLite 資料庫連線,然後 fork(),再嘗試在子處理序中使用該資料庫連線。這將導致各種鎖定問題,而且您很容易會得到一個損毀的資料庫。SQLite 並未設計為支援這種行為。在子處理序中使用的任何資料庫連線都必須在子處理序中開啟,而非從父處理序繼承。
如果連線是在父處理序中開啟的,則不要在子處理序中對資料庫連線呼叫 sqlite3_close()。關閉基礎檔案描述符是安全的,但 sqlite3_close() 介面可能會呼叫清理活動,這些活動會從父處理序中刪除內容,導致錯誤,甚至資料庫損毀。
為了保證資料庫檔案始終一致,SQLite 偶爾會要求作業系統將所有待處理寫入沖洗到永久儲存空間,然後等待沖洗完成。這是在 unix 中使用 fsync() 系統呼叫和在 Windows 中使用 FlushFileBuffers() 來完成的。我們稱這種待處理寫入沖洗為「同步」。
事實上,如果只關注原子和一致的寫入,並且願意放棄耐用的寫入,則同步操作不需要等到內容完全儲存在永久媒體上。相反,可以將同步操作視為 I/O 障礙。只要在同步之前發生的所有寫入都在同步之後發生的任何寫入之前完成,就不會發生資料庫損壞。如果同步作為 I/O 障礙而不是真正的同步運作,則電源故障或系統崩潰可能會導致一個或多個先前提交的交易回滾(違反「ACID」的「耐用」屬性),但資料庫至少會繼續保持一致,而這是大多數人關心的。
不幸的是,大多數消費級大容量儲存裝置都會在同步時說謊。磁碟機將報告內容在到達磁軌緩衝區時就安全地儲存在永久媒體上,而實際上尚未寫入氧化物。這使得磁碟機看起來運作得更快(這對製造商來說至關重要,這樣他們才能在貿易雜誌中顯示良好的基準測試數字)。而且公平地說,只要在磁軌緩衝區實際寫入氧化物之前沒有斷電或硬重置,這種謊言通常不會造成任何危害。但是,如果發生斷電或硬重置,並且導致在同步之後寫入的內容到達氧化物,而同步之前寫入的內容仍位於磁軌緩衝區中,則可能會發生資料庫損壞。
USB 快閃記憶體隨身碟似乎在同步請求方面特別惡劣的說謊者。可以透過在 USB 記憶體隨身碟上提交大型交易到 SQLite 資料庫來輕鬆看到這一點。COMMIT 命令將會相對快速地傳回,表示記憶體隨身碟已告訴作業系統,而作業系統已告訴 SQLite 所有內容都安全地儲存在永久儲存空間中,但記憶體隨身碟末端的 LED 會持續閃爍幾秒鐘。在 LED 仍閃爍時拔出記憶體隨身碟,經常會導致資料庫損壞。
請注意,SQLite 必須相信作業系統和硬體告訴它的同步請求狀態。SQLite 無法偵測到兩者都在說謊,而且寫入可能會發生順序錯誤。不過,在 WAL 模式 下的 SQLite 比在預設的回滾日誌模式下更能容忍順序錯誤的寫入。在 WAL 模式下,只有在 檢查點 操作期間,失敗的同步操作才會導致資料庫損毀。在 COMMIT 期間同步失敗可能會導致持久性遺失,但不會導致資料庫檔案損毀。因此,防止由於同步操作失敗而導致資料庫損毀的一種防禦方式是在 WAL 模式下使用 SQLite,並盡可能減少檢查點的頻率。
SQLite 執行以協助確保完整性的同步操作可以在執行階段使用 synchronous pragma 停用。透過設定 PRAGMA synchronous=OFF,所有同步操作都會被省略。這會讓 SQLite 看起來執行得更快,但它也允許作業系統自由重新排序寫入,如果在所有內容到達永久儲存之前發生斷電或硬體重設,可能會導致資料庫損毀。
為了獲得最大的可靠性和防止資料庫損毀,SQLite 應始終使用其預設的 FULL 同步設定執行。
如果檔案內容因磁碟機或快閃記憶體故障而變更,SQLite 資料庫可能會損毀。這非常罕見,但磁碟偶爾會在扇區中間翻轉一位元。
我們得知,在某些快閃記憶體控制器中,如果在寫入期間電源中斷,磨損平均化邏輯可能會造成隨機的檔案系統損壞。這可能會表現為,例如,在電源中斷時甚至未開啟的檔案中間的隨機變更。因此,例如,當電源中斷時,裝置會將內容寫入快閃記憶體中的 MP3 檔案,而這可能會導致 SQLite 資料庫損毀,即使在電源中斷時資料庫甚至未在使用中。
市面上有許多詐騙的 USB 隨身碟,報告具有高容量(例如:8GB),但實際上只能儲存小得多的容量(例如:1GB)。嘗試寫入這些裝置通常會導致不相關的檔案被覆寫。因此,任何使用詐騙的快閃記憶體裝置都可能輕易導致資料庫損毀。例如「假容量 usb」等網際網路搜尋會顯示許多關於此問題的令人不安的資訊。
SQLite 是 C 函式庫,在與其所服務的應用程式相同的位址空間中執行。這表示應用程式中的雜散指標、緩衝區溢位、堆損毀或其他故障可能會損毀內部 SQLite 資料結構,並最終導致資料庫檔案損毀。通常,這些類型的問題會在發生任何資料庫損毀之前表現為段落錯誤,但曾有應用程式程式碼錯誤導致 SQLite 發生細微故障的案例,進而損毀資料庫檔案,而不是發生恐慌。
當使用記憶體對應 I/O時,記憶體毀損問題會變得更嚴重。當資料庫檔案全部或部分對應到應用程式的位址空間時,一個覆寫對應空間任何部分的游標會立即毀損資料庫檔案,而不需要應用程式執行後續的 write() 系統呼叫。
作業系統有時會表現出非標準行為,這可能會導致問題。有時這種非標準行為是故意的,有時是實作中的錯誤。但無論如何,如果作業系統執行方式與 SQLite 預期的不同,就有可能毀損資料庫。
某些較舊版本的 Linux 使用 LinuxThreads 函式庫來支援執行緒。LinuxThreads 類似於 Pthreads,但處理 POSIX 建議鎖定時有細微的差異。SQLite 版本 2.2.3 到 3.6.23 識別執行時正在使用 LinuxThreads,並採取適當的動作來解決 LinuxThreads 的非標準行為。但大多數現代 Linux 實作都使用較新且正確的 Pthreads NPTL 實作。從 SQLite 版本 3.7.0(2010-07-21)開始,假設使用 NPTL。不會進行檢查。因此,如果在使用 LinuxThreads 的較舊 Linux 系統上執行多執行緒應用程式,最新版本的 SQLite 會出現細微的故障,並可能毀損資料庫檔案。
QNX 上的 mmap() 存在一些微妙的問題,導致對單一檔案描述子進行第二次 mmap() 呼叫時,可能會將從第一次 mmap() 呼叫取得的記憶體歸零。Unix 上的 SQLite 使用 mmap() 在 WAL 模式 中建立共享記憶體區域以進行交易協調,而且在大型交易中會呼叫 mmap() 多次。在這種情況下,已證實 QNX mmap() 會損毀資料庫檔案。QNX 工程師已知悉此問題,並正在努力找出解決方案;當您閱讀這篇文章時,問題可能已經獲得解決。
在 QNX 上執行時,建議永遠不要使用 記憶體對應 I/O。此外,若要使用 WAL 模式,建議應用程式採用 獨佔鎖定模式,以使用 不使用共享記憶體的 WAL。
由於 SQLite 資料庫是普通的磁碟檔案,因此檔案系統中的任何故障都可能損毀資料庫。現代作業系統中的檔案系統非常可靠,但錯誤仍然會發生。例如,在 2013-10-01,存放 Tcl/Tk Wiki 的 SQLite 資料庫在主機電腦移轉到檔案系統層有問題的 (Linux) 核心不穩定版本後幾天損毀。在那個事件中,檔案系統最後損毀得如此嚴重,導致機器無法使用,但問題最早的徵兆是 SQLite 資料庫損毀。
SQLite 有許多內建防護措施,可防止資料庫損毀。但許多這些防護措施都可以透過組態選項停用。如果停用防護措施,則資料庫可能會損毀。
以下是停用 SQLite 內建防護機制的範例
設定 PRAGMA synchronous=OFF 可能會導致資料庫在作業系統崩潰或電源故障時損毀,儘管此設定不會因應用程式崩潰而造成損毀。
在其他資料庫連線開啟時變更 PRAGMA schema_version。
使用 PRAGMA journal_mode=OFF 或 PRAGMA journal_mode=MEMORY,並在寫入交易中發生應用程式崩潰。
設定 PRAGMA writable_schema=ON,然後使用 DML 陳述式變更資料庫結構,如果沒有小心執行,可能會導致資料庫完全無法讀取。
SQLite 經過 非常仔細的測試,以確保它盡可能沒有錯誤。在每個 SQLite 版本執行的許多測試中,包括模擬電源故障、I/O 錯誤和記憶體不足 (OOM) 錯誤的測試,並驗證在這些事件期間不會發生任何資料庫損毀。SQLite 也經過實地驗證,約有 20 億次部署,沒有發生任何嚴重問題。
儘管如此,沒有任何軟體是 100% 完美的。SQLite 中有一些歷史錯誤 (現已修正) 可能會導致資料庫損毀。而且可能還有更多尚未發現的錯誤。由於 SQLite 經過廣泛測試和使用,導致資料庫損毀的錯誤往往非常模糊。應用程式遇到 SQLite 錯誤的機率很小。為了說明這一點,以下說明 2009-04-01 至 2013-04-15 這四年期間在 SQLite 中發現的所有資料庫損毀錯誤。此說明應讓讀者直覺地了解 SQLite 中哪些類型的錯誤設法通過測試程序並進入版本中。
如果資料庫是由 SQLite 版本 3.7.0 或更新版本寫入,然後再由 SQLite 版本 3.6.23 或更早版本寫入,並以使資料庫檔案大小減小的方式寫入,則下次 SQLite 版本 3.7.0 存取資料庫檔案時,它可能會報告資料庫檔案已損毀。然而,資料庫檔案並未真正損毀。版本 3.7.0 在其損毀偵測中只是過於謹慎。
此問題已於 2011-02-20 修復。此修正程式最先出現在 SQLite 3.7.6 版(2011-04-12)。
在一個程序或執行緒中,重複將 SQLite 資料庫切換進出 WAL 模式,並在切換之間執行 VACUUM 指令,可能會導致另一個已開啟資料庫檔案的程序或執行緒錯過資料庫已變更的事實。第二個程序或執行緒可能會嘗試使用過期的快取修改資料庫,並造成資料庫毀損。
此問題是在內部測試期間發現,且從未在實際環境中觀察到。此問題已於 2011-01-27 和 3.7.5 版中修復。
如果作業系統在嘗試取得 WAL 模式 中共享記憶體的特定鎖定時傳回 I/O 錯誤,則 SQLite 可能無法重設快取,如果嘗試後續寫入,可能會導致資料庫毀損。
請注意,此問題僅在嘗試取得鎖定時導致 I/O 錯誤時發生。如果僅未授予鎖定(因為其他執行緒或程序已持有衝突鎖定),則永遠不會發生毀損。我們不知道任何作業系統會在嘗試取得共享記憶體的檔案鎖定時發生 I/O 錯誤。因此,這是一個理論問題,而非實際問題。不用說,此問題從未在實際環境中觀察到。此問題是在模擬 I/O 錯誤的測試架構中對 SQLite 進行壓力測試時發現的。
此問題已於 2010-09-20 修復,適用於 SQLite 3.7.3 版。
當內容從 SQLite 資料庫刪除時,不再使用的頁面會新增至免費清單,並重複使用來儲存後續插入新增的內容。SQLite 中一個存在於版本 3.6.16 至 3.7.2 的錯誤可能會導致頁面在使用 incremental_vacuum 時從免費清單中遺失。這不會造成資料遺失。但會導致資料庫檔案比必要的大。而且會導致 integrity_check pragma 回報免費清單中遺失頁面。
此問題已於 2010-08-23 修復,適用於 SQLite 版本 3.7.2。
SQLite 版本 3.7.0 為 SQLite 資料庫檔案格式引入了許多新的強化功能(例如,但不僅限於 WAL)。3.7.0 版本是這些新功能的試用版。我們預期會發現問題,而且沒有失望。
如果資料庫最初是使用 SQLite 版本 3.7.0 建立,然後由 SQLite 版本 3.6.23.1 編寫,使得資料庫檔案大小增加,然後再由 SQLite 版本 3.7.0 編寫,資料庫檔案可能會損毀。
此問題已於 2010-08-04 修復,適用於 SQLite 版本 3.7.1。
SQLite 版本 3.7.16.2 修正了 Windows 系統鎖定邏輯中的細微競爭條件。當資料庫檔案需要復原,因為先前寫入它的程序在交易過程中崩潰,而兩個或更多程序同時嘗試開啟該資料庫時,競爭條件可能會導致其中一個程序錯誤地顯示復原已完成,允許該程序繼續使用資料庫檔案,而未先執行復原。如果該程序寫入檔案,則檔案可能會損毀。此競爭條件顯然存在於所有先前版本的 Windows SQLite 中,可追溯至 2004 年。但競爭非常激烈。實際上,您需要一台快速的多分核心電腦,在其中啟動兩個程序,以便在兩個獨立的核心上同時執行復原。此缺陷僅存在於 Windows 系統中,且不影響 posix 作業系統介面。
當使用 SAVEPOINT 啟動嵌套交易時,SQLite 會使用次要回滾記錄檔來追蹤嵌套交易的變更,以防需要回滾內部交易。次要記錄檔不會參與保護資料庫,使其免於因程式崩潰或停電而損毀。次要記錄檔僅在回滾嵌套交易的內部交易時才會發揮作用。
這些次要記錄檔可以儲存在記憶體中或作為磁碟上的暫時檔案。預設行為是將它們儲存在磁碟上。但可以使用 -DSQLITE_TEMP_STORE 編譯時期選項,或在執行階段使用 PRAGMA temp_store 陳述式來變更它。只有當次要記錄檔儲存在記憶體中時,才會出現錯誤。
在 SQLite 版本 3.35.0(2021-03-12)中,新增了一項最佳化,讓 SQLite 在記憶體中保留次要日誌時,使用的記憶體會更少。不幸的是,新邏輯中的邊界檢查編寫錯誤。原本應該使用「<」運算子,卻編寫成「<="。如果次要日誌回滾,這個錯誤可能會讓它進入不一致的狀態。如果進行其他變更,而外部交易最終提交,資料庫可能會處於不一致的狀態。
這個問題是由一位 獨立研究人員 發現的,他嘗試使用模糊測試器找出 SQLite 中的錯誤。模糊測試器在 assert() 敘述 中找到一個失敗,用於協助驗證次要日誌的內部狀態。這個錯誤是一個相當難以察覺的特殊情況,如果不是 SQLite 中廣泛使用 assert() 敘述、安全研究人員的堅持和韌性,以及他們自訂的最新模糊測試器,這個錯誤可能會被忽略很多年。
這個問題已在 版本 3.37.2(2022-01-06)中 修正。
此頁面最後修改於 2023-10-10 17:29:48 UTC