小巧、快速、可靠
任選三項
從損毀的 SQLite 資料庫復原資料

1. 從損毀的 SQLite 資料庫復原(部分)資料

SQLite 資料庫非常強健。應用程式故障和電源中斷通常不會影響資料庫內容的完整性。不過,還是有可能損毀 SQLite 資料庫。例如,硬體故障可能會損壞資料庫檔案,或是惡意程序可能會開啟資料庫並覆寫部分內容。

如果資料庫檔案損毀,有時會希望盡可能從檔案中搶救資料。復原 API 就是為了協助這項工作而設計的。

1.1. 限制

有時可以完美復原損毀的資料庫,但這只是例外。通常復原的資料庫會在許多方面有缺陷

復原 API 會盡可能地復原資料庫,但結果總是可疑的。有時(例如如果損毀僅限於索引),復原會完美復原資料庫內容。然而在其他情況下,復原會不完美。這種不完美的影響取決於應用程式。一個儲存書籤清單的資料庫在復原後仍然是書籤清單。復原後可能會遺失、新增或變更一些書籤,但清單一開始就是「模糊」且不完美的,因此增加一點不確定性並不會對應用程式造成致命影響。但如果會計資料庫損毀並隨後復原,帳目可能會不平衡。

最好將復原 API 視為一項搶救工作。復原會從舊資料庫的殘骸中提取盡可能多的可用資料,但有些部分可能損毀到無法修復,在將復原的資料庫恢復到服務之前,應執行一些返工和測試。

2. 在 CLI 中使用「.recover」指令進行復原

手動復原損毀資料庫最簡單的方法是使用 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 值)。

3. 將復原 API 建置到應用程式中

3.1. 原始碼檔案

如果您要將復原 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

3.2. 如何實作復原

以下是從損毀的資料庫中復原內容所需的基本步驟

  1. 透過呼叫 sqlite3_recover_init() 或 sqlite3_recover_init_sql() 建立 sqlite3_recover 處理常式。使用 sqlite3_recover_init() 將復原的內容儲存在個別資料庫中,並使用 sqlite3_recover_init_sql() 產生將重建資料庫的 SQL 文字。

  2. 呼叫 sqlite3_recover_config() 零次或多次,以設定新的 sqlite3_recovery 處理常式的選項。

  3. 重複呼叫 sqlite3_recover_step(),直到它傳回非 SQLITE_OK 的值。如果它傳回 SQLITE_DONE,表示復原作業已完成且無錯誤。如果它傳回其他非 SQLITE_OK 的值,表示已發生錯誤。sqlite3_recover_run() 介面也可用作便利封裝,它會重複呼叫 sqlite3_recover_step(),直到它傳回非 SQLITE_DONE 的值。

  4. 分別使用 sqlite3_recover_errcode() 和 sqlite3_recover_errmsg() 介面,擷取任何錯誤代碼和英文錯誤訊息。

  5. 呼叫 sqlite3_recover_finish() 來銷毀 sqlite3_recover 物件。

介面的詳細資料說明在 sqlite3_recover.h 標頭檔案 的註解中。

3.3. 範例實作

SQLite 本身如何使用復原擴充功能的範例,請參閱下列連結