SQLite 数据库非常可靠。应用程序故障和电源故障通常不会影响数据库内容。但是,可能会损坏 SQLite 数据库。例如,硬件故障可能会损坏数据库文件,或者恶意进程可能会打开数据库并覆盖部分内容。
对于损坏的数据库文件,有时需要尝试从文件中尽可能多地恢复数据。恢复 API 旨在促进此操作。
有时可以完美地恢复损坏的数据库,但这只是例外情况。通常,恢复的数据库会在多个方面存在缺陷
部分内容可能被永久删除且无法恢复。例如,如果恶意进程覆盖了数据库文件的一部分,就会发生这种情况。
先前删除的内容可能会重新出现。通常,当 SQLite 执行 DELETE 操作时,它不会实际覆盖旧内容,而是会记住该空间在下次 INSERT 时可供重用。如果在尝试恢复时此类删除的内容仍在文件中,它可能会被提取并“恢复”。
恢复的内容可能会被更改。例如,存储在特定行中的值可能会从 48 更改为 49。或者它可能会从整数更改为字符串或 BLOB。原本为 NULL 的值可能会变为整数。字符串值可能会变为 BLOB。依此类推。
恢复后约束可能无效。CHECK 约束、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 如果提供此选项,则不会从损坏的数据库中提取不属于 INTEGER PRIMARY KEY 值的 rowid 值。
如果要将恢复 API 构建到应用程序中,您需要在通常的“sqlite3.c”和“sqlite3.h”源文件之外向构建添加一些源文件。您需要
sqlite3recover.c 这是实现恢复 API 的主要源文件。 sqlite3recover.h 这是与 sqlite3recover.h 对应的头文件。 dbdata.c 此文件实现两个名为“sqlite_dbdata”和“sqlite_dbptr”的虚拟表,这些表是 sqlite3recover.c 所需的。
上面的两个 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.ac.cn/src/info/30475c820dc5ab8a8?ln=999,1026
SQLite 树中“fuzzcheck”测试实用程序中找到的恢复扩展示例。
https://sqlite.ac.cn/src/info/84bb08d8762920285f08f1c0?ln=7299,7361
在CLI中实现 ".recover" 命令的代码。