SQLite 資料庫非常強健。應用程式故障和電源中斷通常不會影響資料庫內容的完整性。不過,還是有可能損毀 SQLite 資料庫。例如,硬體故障可能會損壞資料庫檔案,或是惡意程序可能會開啟資料庫並覆寫部分內容。
如果資料庫檔案損毀,有時會希望盡可能從檔案中搶救資料。復原 API 就是為了協助這項工作而設計的。
有時可以完美復原損毀的資料庫,但這只是例外。通常復原的資料庫會在許多方面有缺陷
有些內容可能會永久刪除且無法復原。例如,如果惡意程序覆寫了資料庫檔案的一部分,就會發生這種情況。
先前刪除的內容可能會再次出現。SQLite 執行 DELETE 作業時,通常不會覆寫舊內容,而是記住空間可用於下次 INSERT 時重複使用。如果在嘗試復原時,此類已刪除的內容仍在檔案中,可能會將其萃取出來並「復活」。
復原的內容可能會被變更。例如,儲存在特定列中的值可能會從 48 變更為 49。或者,它可能會從整數變更為字串或 blob。原本為 NULL 的值可能會變成整數。字串值可能會變成 BLOB,依此類推。
限制在復原後可能無效。檢查限制、FOREIGN KEY 限制、UNIQUE 限制、STRICT 表格 上的類型限制 - 這些限制中的任何一個都可能在復原的資料庫中遭到破壞。
內容可能從一個表格移動到另一個表格中。
復原 API 會盡可能地復原資料庫,但結果總是可疑的。有時(例如如果損毀僅限於索引),復原會完美復原資料庫內容。然而在其他情況下,復原會不完美。這種不完美的影響取決於應用程式。一個儲存書籤清單的資料庫在復原後仍然是書籤清單。復原後可能會遺失、新增或變更一些書籤,但清單一開始就是「模糊」且不完美的,因此增加一點不確定性並不會對應用程式造成致命影響。但如果會計資料庫損毀並隨後復原,帳目可能會不平衡。
最好將復原 API 視為一項搶救工作。復原會從舊資料庫的殘骸中提取盡可能多的可用資料,但有些部分可能損毀到無法修復,在將復原的資料庫恢復到服務之前,應執行一些返工和測試。
手動復原損毀資料庫最簡單的方法是使用 SQLite 的命令列介面或「CLI」。CLI 是一個名為「sqlite3」的程式。使用它來復原損毀的資料庫檔案,使用類似於以下的指令
sqlite3 corrupt.db .recover >data.sql
這會在名為「data.sql」的檔案中產生 SQL 文字,可用於重建原始資料庫
sqlite3 recovered.db <data.sql
「.recover」選項實際上是發佈到 CLI 的指令。該指令可以接受參數。例如,透過執行
sqlite3 corruptdb ".recover --ignore-freelist" >data.sql
請注意,「.recover」指令及其參數必須包含在引號中。支援下列選項
--ignore-freelist 忽略資料庫中看似是 freelist 一部分的頁面。一般會掃描 freelist,如果它包含看似有內容的頁面,就會輸出該內容。但如果該頁面真的在 freelist 中,這表示先前刪除的資訊會重新導入資料庫。
--lost-and-found TABLE 如果在復原過程中發現無法與特定表格關聯的內容,就會將其放入「lost_and_found」表格中。使用此選項將「lost_and_found」表格的名稱變更為「TABLE」。
--no-rowids 如果提供此選項,則不會從損毀的資料庫中擷取 rowid 值(也不包含 INTEGER PRIMARY KEY 值)。
如果您要將復原 API 建置到您的應用程式中,您需要在建置中新增一些原始碼檔案,除了平常的「sqlite3.c」和「sqlite3.h」原始碼檔案之外。您需要
sqlite3recover.c 這是實作復原 API 的主要原始碼檔案。 sqlite3recover.h 這是與 sqlite3recover.h 搭配使用的標頭檔。 dbdata.c 此檔案實作兩個 sqlite3recover.c 所需的虛擬表格名稱「sqlite_dbdata」和「sqlite_dbptr」。
上述兩個 C 原始碼檔案需要以與連結「sqlite3.c」相同的方式連結到您的應用程式中。而且在編譯 C 檔案時,編譯器需要能夠存取標頭檔。
此外,應用程式(或更具體地說,連結到應用程式的 sqlite3.c)必須使用下列選項編譯
-DSQLITE_ENABLE_DBPAGE_VTAB
以下是從損毀的資料庫中復原內容所需的基本步驟
透過呼叫 sqlite3_recover_init() 或 sqlite3_recover_init_sql() 建立 sqlite3_recover 處理常式。使用 sqlite3_recover_init() 將復原的內容儲存在個別資料庫中,並使用 sqlite3_recover_init_sql() 產生將重建資料庫的 SQL 文字。
呼叫 sqlite3_recover_config() 零次或多次,以設定新的 sqlite3_recovery 處理常式的選項。
重複呼叫 sqlite3_recover_step(),直到它傳回非 SQLITE_OK 的值。如果它傳回 SQLITE_DONE,表示復原作業已完成且無錯誤。如果它傳回其他非 SQLITE_OK 的值,表示已發生錯誤。sqlite3_recover_run() 介面也可用作便利封裝,它會重複呼叫 sqlite3_recover_step(),直到它傳回非 SQLITE_DONE 的值。
分別使用 sqlite3_recover_errcode() 和 sqlite3_recover_errmsg() 介面,擷取任何錯誤代碼和英文錯誤訊息。
呼叫 sqlite3_recover_finish() 來銷毀 sqlite3_recover 物件。
介面的詳細資料說明在 sqlite3_recover.h 標頭檔案 的註解中。
SQLite 本身如何使用復原擴充功能的範例,請參閱下列連結
https://sqlite.dev.org.tw/src/info/30475c820dc5ab8a8?ln=999,1026
SQLite 樹狀結構中「fuzzcheck」測試工具中找到的復原擴充功能範例。
https://sqlite.dev.org.tw/src/info/84bb08d8762920285f08f1c0?ln=7299,7361
在 CLI 中實作「.recover」指令的程式碼。