SQLite データベースファイル形式
SQLite のデータベースファイル。単一ファイルで完結する組み込み SQL データベース。
- MIME タイプ
- application/vnd.sqlite3
- 拡張子
- .sqlite, .db, .sqlite3
- カテゴリ
- データ・データベース
マジックナンバー(ファイル署名)
| 0x00000000 | 53514C69746520666F726D6174203300 | SQLite format 3· | 'SQLite format 3\0' header |
SQLite データベース形式について
SQLite のデータベースは、すべて同じ大きさの固定サイズページを並べたもので、1 ページ目の先頭 100 バイトがファイル全体を説明するヘッダに割り当てられています。それ以外はすべて(テーブルもインデックスもスキーマ自身も)そのページで組んだ B-tree です。スキーマにしても、他と同じように 1 ページ目に置かれた sqlite_schema というテーブルにすぎません。この均質さが形式の安定性を支えていて、2004 年に SQLite 3.0 が書いたファイルは今日でも開きますし、プロジェクトは 2050 年まで読めるようにすると公言しています。
ヘッダーの構造(バイト単位)
| オフセット | 長さ | フィールド | 意味 |
|---|---|---|---|
| 0 | 16 | Header string | ASCII の 'SQLite format 3' に NUL バイトが 1 つ続く、ちょうど 16 バイト。3 はライブラリのバージョンではなくファイル形式の世代で、2004 年から変わっていません。 |
| 16 | 2 | Page size | ビッグエンディアンで、デスクトップで出会う数少ないビッグエンディアンのフィールドの 1 つです。512 から 32768 までの 2 のべき乗、または 2 バイトに収まらないため値 1 で 65536 を表します。今どきの既定は 4096 なので、たいてい 10 00 と読めます。 |
| 18 | 1 | File format write version | 1 なら従来のロールバックジャーナル、2 なら write-ahead logging。WAL モードのデータベースは相棒の -wal と -shm が揃っている必要があります。稼働中のデータベースから .db だけをコピーすると直近のコミットを失うのはこのためです。 |
| 19 | 1 | File format read version | 1 か 2 で意味は上と同じ。2 より大きい値は「このファイルは理解できない何かを使っている」と古いライブラリに伝えるもので、その場合は推測せず拒否しなければなりません。 |
| 20 | 1 | Reserved space per page | 各ページの末尾に未使用として残すバイト数で、通常は 0 です。SEE のような暗号化拡張は、ページごとの nonce と認証タグを置くためにここを設定します。開けないファイルでここが非ゼロなら、それが理由です。 |
| 21 | 1 | Maximum embedded payload fraction | 64 でなければなりません。調整可能にする気で作られながら結局そうならなかったつまみなので、これ以外の値は珍しい設定ではなくヘッダの破損を意味します。 |
| 22 | 1 | Minimum embedded payload fraction | 32 でなければなりません。上下のバイトと合わせた 64、32、32 という並びが、このヘッダで一番安上がりな整合性チェックです。 |
| 23 | 1 | Leaf payload fraction | 32 でなければなりません。固定されている理由は上と同じ歴史的事情で、設定可能にするつもりが最後までそうならなかったのです。 |
| 24 | 4 | File change counter | ファイルを変更するトランザクションのたびに増えます。読む側はこれでキャッシュしたページが古くなったことに気づけるので、前回見たときからデータベースに書き込みがあったかを確かめる一番手早い方法でもあります。 |
| 28 | 4 | Database size in pages | ヘッダに書かれたサイズです。信用してよいのはオフセット 92 の version-valid-for と一致するときだけで、そうでなければ本当のサイズはファイル長 ÷ ページサイズです。このフィールドを維持しない古いライブラリが書き込んだ可能性があるからです。 |
| 32 | 4 | First freelist trunk page | 未使用ページのリストが始まるページ番号、無ければ 0。削除された行のページはファイルを縮めるのではなくこのリストに積まれます。VACUUM を実行するまでデータベースが小さくならないのはそのためです。 |
| 36 | 4 | Freelist page count | そのリストに乗っているページ数。データベースの規模に対してこの値が大きければ、大量に削除して領域を抱えたままのファイルだという印であり、同時に削除済みの行が hex エディタでまだ拾える状態だという印でもあります。 |
| 40 | 4 | Schema cookie | スキーマが変わるたびに増えます。プリペアドステートメントはコンパイル時の値を覚えていて、この値が動くと自動的に再コンパイルされます。接続を開いたまま ALTER TABLE を打てるのはこの仕組みのおかげです。 |
| 44 | 4 | Schema format number | 1 から 4。現行は 4 で、2006 年以降の既定です。DESC インデックスや真偽値リテラルはこれで有効になります。知らない形式番号を見た古いライブラリは、読み違えるのではなくファイルを拒否します。 |
| 48 | 4 | Default page cache size | PRAGMA default_cache_size が設定する、ページキャッシュへの提案値です。あくまで助言でありライブラリは無視してかまいません。最近のバージョンはおおむね無視します。 |
| 52 | 4 | Largest root b-tree page | auto-vacuum または incremental-vacuum のときだけ非ゼロで、ポインタマップを維持できるよう最大のルートページを記録します。ゼロなら、そのデータベースが自力で縮むことはありません。 |
| 56 | 4 | Text encoding | 1 が UTF-8、2 が UTF-16 リトルエンディアン、3 が UTF-16 ビッグエンディアン。データベース作成時に 1 度だけ決まり、以後変更できません。実質すべての実装が書くのは UTF-8 で、ここを取り違えて文字列を読むと文字化けだらけのデータベースになります。 |
| 60 | 4 | User version | SQLite 自身は触らず、PRAGMA user_version で読み書きします。アプリはこれをスキーマ移行のカウンタとして使うので、どのバージョンのアプリが書いたデータベースかを突き止めたいときには、ファイル中で最も役に立つ 4 バイトになります。 |
| 64 | 4 | Incremental vacuum mode | incremental-vacuum が有効なら非ゼロ。意味を持つのは、オフセット 52 の最大ルートページも非ゼロのときだけです。 |
| 68 | 4 | Application ID | PRAGMA application_id が設定する値で、プログラムがこのデータベースファイルを自分の形式だと名乗るためのものです。アプリがちゃんと設定していれば、判定器が「これは SQLite です」ではなく「これは Firefox の places データベースです」と言えるようになります。 |
| 72 | 20 | Reserved for expansion | ゼロでなければならない 20 バイト。20 年間ゼロのままなので、ここに非ゼロが並んでいれば、何かがヘッダを上書きした確かな証拠になります。 |
| 92 | 4 | Version valid for number | 下のバージョン番号が最後に書かれたときの変更カウンタの値です。オフセット 24 の変更カウンタと一致しないなら、古いライブラリがファイルを変更したということであり、オフセット 28 のヘッダ内サイズを信用してはいけません。 |
| 96 | 4 | SQLITE_VERSION_NUMBER | 最後にこのファイルを書いたライブラリのバージョンで、メジャー × 100 万 + マイナー × 1000 + パッチとして符号化されています。0x002E8DF8 は 3051000、つまり SQLite 3.51.0。どのビルドが最後にこのデータベースを触ったかがこの 4 バイトで正確に分かります。 |
似た形式との見分け方
| 紛らわしい形式 | 見分け方 |
|---|---|
| WAL mode | 先頭 16 バイトは完全に同一で、違いはオフセット 18 と 19 の 2 バイトが 1 ではなく 2 になっていることだけです。これが重要なのは、最新のコミット済みデータが .db ファイルの中にまったく無い可能性があるからです。それは相棒の -wal ファイルの中にあります。データベースだけをコピーすると、トランザクションが黙って失われます。先にチェックポイントを打つか、3 つのファイルをまとめてコピーしてください。 |
| Encrypted SQLite | SQLCipher などはヘッダを含むファイル全体を暗号化するので、オフセット 0 は乱数めいたバイト列になり、どの判定器も SQLite とは呼びません。SEE 拡張は逆の方針を取り、ヘッダは読める状態のまま、オフセット 20 の予約領域バイトを非ゼロにします。SQLite と判定されるのに開けないファイルは、オフセット 20 を確認する価値があります。 |
| OLE2 / CFB | 「1 ファイルの中のファイルシステム」という汎用形式が 2 つ。SQLite は読める ASCII 16 バイトで始まり、Compound File Binary コンテナは D0 CF 11 E0 A1 B1 1A E1 で始まります。どちらも固定サイズページを使い、フリーリストを持ち、書き込み途中の中断に弱いのも同じ。ただし SQLite の形式は公開・版管理され、フィールド単位まで文書化されています。CFB は最初の 15 年間そうではありませんでした。 |
| Parquet | 「データを 1 ファイルで配るならどっち」という話になると必ず出てくる組み合わせです。Parquet は列指向・不変で、両端に 'PAR1' の印が付きます。SQLite は行指向・トランザクション対応で、その場で更新できます。10 億行の 1 列を走査するなら Parquet の圧勝、インデックスや結合、行の書き戻しが要るなら SQLite の勝ちです。 |
よくある壊れ方
- -wal や -journal のファイルを失った。データベース本体は完全に無傷なのに読み込みが失敗したり、黙ってロールバックされたりします。コピーした時点でトランザクションが飛んでいたからです。これは .db の破損ではなく不完全なコピーであり、人が SQLite のデータベースを壊す方法として断然多いパターンです。
- 書き込み中のファイルをコピーした。コミットは複数のページに触れるので、トランザクションの途中でスナップショットを取ると、古いページと新しいページが混ざった、どの時点にも存在しなかった状態が撮れてしまいます。ヘッダは完璧にパースでき、ページ数ももっともらしく、B-tree をたどっていくと別のテーブルのページに落ちます。cp ではなくバックアップ API か VACUUM INTO を使ってください。
- テキストモード転送、あるいはエディタがファイルを書き直した。多バイトのフィールドはすべてバイナリで、ヘッダの ASCII 部分には 0D 0A が含まれていないので、そこだけ無傷で残り、それ以降のページ境界が全部ずれます。ファイルは依然 SQLite と識別され、1 ページ目から先が読めません。
- ダウンロードの途中切れ。オフセット 28 のヘッダは、ファイルに収まりきらないページ数を宣言することになります。SQLite が確認しているのはまさにこれで、宣言されたサイズと、ファイル長 ÷ オフセット 16 のページサイズを比べます。短くなったファイルでも読み取り専用で開けることがあり、切断より前にページが収まったテーブルはそのまま使えます。
- 他のプログラムがヘッダのバイトを上書きした。オフセット 21、22、23 の 3 つの定数は 64、32、32 でなければならず、オフセット 72 からの 20 バイトはゼロでなければなりません。踏み荒らされたヘッダを検出する一番安上がりなテストがこれで、その名前を自分のものだと思い込んだ何かが書き込んだ場合を捕まえられます。
この形式の現在地
SQLite は世界で最も広く配備されているデータベースエンジンであり、その座を降りる気配もありません。Android と iOS の全端末、主要ブラウザ、macOS、無数のデスクトップアプリ、そして大量の組み込みファームウェアが、状態を SQLite のファイルに置いています。データに構造があり、問い合わせが必要で、1 台のマシンに収まるなら、まず第一候補です。アプリの設定、キャッシュ、メッセージ履歴、オフラインストア、ローカルの集計。サーバーも設定もネットワークも要らずに、本物のトランザクションと本物の SQL が手に入ります。向いていないのは複数のマシンから同時に書き込む場面で、ロックがファイルベースなのでネットワークファイルシステムでは破綻します。そこが PostgreSQL や MySQL の出番の境界線です。近年はデータベースというよりアプリのファイル形式としての採用も増えました。1 つの .sqlite がプロジェクト一式を運び、全体を書き直さずにその場で更新でき、保存の途中でクラッシュしても壊れにくい。自作のバイナリ形式ではまず得られない性質です。形式の安定性が明文化されていること、そしてテストスイートがライブラリ本体より大きいことを考えれば、アーカイブを賭ける相手としては妥当な選択です。
SQLite データベースファイルをこのサイトで開く
どれもブラウザ内で動きます。ファイルが端末から出ることはありません。
関連する形式
SQLite データベースと混同しやすい形式です。先頭バイトを見比べてください。
| 形式 | 先頭バイト | 拡張子 |
|---|---|---|
| Parquet | 50 41 52 31 | .parquet |
| OLE2 / CFB | D0 CF 11 E0 A1 B1 | .doc |