🏥

SQLite3 の DB が壊れたときの修復方法

2023/04/29に公開

壊れるとこうなる

$ sqlite3 broken.db "SELECT COUNT(*) FROM articles"
Error: stepping, database disk image is malformed (11)

SELECT 1 のような SQL だと通ってしまうので存在するはずのテーブルにアクセスする。本当は articles テーブルが存在するはずだが読めていない。

修復

rm -f new.db
sqlite3 broken.db ".recover" | sqlite3 new.db

new.db は新規作成する予定の DB だが、すでに存在すると追加になってしまう。追加になるとユニーク制約のエラーなどが多発して混乱の元なので実行前に消しておく。

直ったのを確認する

$ sqlite3 new.db "SELECT COUNT(*) FROM articles"
11009

.recover のかわりに .dump だとダメ

$ rm -f new.db
$ sqlite3 broken.db ".dump" | sqlite3 new.db
$ sqlite3 new.db "SELECT COUNT(*) FROM articles"
Error: in prepare, no such table: articles

ChatGPT の言う通りにしてもダメ

$ sqlite3 broken.db ".dump" > backup.sql

$ sqlite3 broken.db "PRAGMA integrity_check"
*** in database main ***
Page 7220: btreeInitPage() returns error code 11
On tree page 6178 cell 397: Rowid 11475 out of order
Page 5044 is never used
(snip)

$ sqlite3 broken.db "VACUUM"
Error: stepping, database disk image is malformed (11)

$ sqlite3 broken.db "SELECT COUNT(*) FROM articles"
Error: stepping, database disk image is malformed (11)

ChatGPT の提案は、まずバックアップしたのち PRAGMA integrity_check でエラーがでれば VACUUM を実行しろ、という内容だったが解決できなかった。

そもそもなんで壊れた?

仮説1. 異なるバージョンで更新

sqlite3-1.5.2-x86_64-darwin で作ったものを sqlite3-1.6.2-x86_64-darwin で更新したせいかと考えたがどちらも 3 系だし、常識的に考えてバージョンが異なる程度で互換性がなくなっていたら誰も使ってないだろう。

仮説2. 書き込み中にコピー

書き込み中にコピーする意図はなかったが cp file.db backup.db としたタイミングが余程悪かったと思われる。sqlite3 file.db ".backup backup.sql" とするべきだった。

参照

https://stackoverflow.com/questions/18259692/how-to-recover-a-corrupt-sqlite3-database/57872238#57872238

Discussion