# 機能設計書 21-ページャー

## 概要

本ドキュメントは、SQLiteのページャー（Pager）サブシステムの機能設計を記述する。ページャーはデータベースファイルへのページ単位アクセス、トランザクションのアトミックコミット・ロールバック機能を実装する中核コンポーネントである。

### 本機能の処理概要

ページャーはSQLiteのストレージ層において、B-Treeとディスク間のインターフェースとして機能し、データベースページの読み書きとトランザクションの原子性を保証する。

**業務上の目的・背景**：データベースシステムにおいて、トランザクションの原子性（Atomicity）と永続性（Durability）を確保することは不可欠である。ページャーはジャーナルファイルを使用したロールバック機構により、電源障害やシステムクラッシュが発生した場合でもデータ整合性を維持する。これにより、アプリケーション開発者は複雑なリカバリロジックを実装する必要なく、信頼性の高いデータ操作を行える。

**機能の利用シーン**：
- データベースファイルの読み取り・書き込み操作
- トランザクションの開始・コミット・ロールバック
- ホットジャーナルからのクラッシュリカバリ
- 複数プロセス間のファイルロック制御

**主要な処理内容**：
1. データベースファイルのページ単位での読み書き
2. ジャーナルファイルによるトランザクションログの管理
3. アトミックコミットの実現（書き込み前にジャーナルへ元データを保存）
4. ロールバック処理（ジャーナルから元データを復元）
5. ファイルロック管理（SHARED/RESERVED/EXCLUSIVE）
6. WALモードとの連携

**関連システム・外部連携**：VFS（Virtual File System）を通じてOSのファイルシステムにアクセスする。B-Treeモジュールからの要求を受けてページを提供する。

**権限による制御**：ファイルロックレベル（NO_LOCK、SHARED_LOCK、RESERVED_LOCK、EXCLUSIVE_LOCK）により読み書き権限を制御する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | Speedtest1（メインスレッド版） | 主機能 | ベンチマーク対象のページアクセス |

## 機能種別

データアクセス / トランザクション管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pVfs | sqlite3_vfs* | Yes | VFSインスタンス | NULLでないこと |
| zFilename | const char* | No | データベースファイルパス | NULLの場合は一時ファイル |
| nExtra | int | Yes | 各ページに追加するバイト数 | 0以上 |
| flags | int | Yes | オープンフラグ | PAGER_OMIT_JOURNAL, PAGER_MEMORY |
| vfsFlags | u32 | Yes | VFSオープンフラグ | SQLITE_OPEN_* |

### 入力データソース

- B-Treeモジュールからのページ読み書き要求
- 上位層からのトランザクション制御コマンド

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| DbPage* | ポインタ | 要求されたページへのポインタ |
| rc | int | 操作結果（SQLITE_OK等） |

### 出力先

- ページキャッシュ経由でB-Treeモジュールへ
- データベースファイル（VFS経由）
- ジャーナルファイル（VFS経由）

## 処理フロー

### 処理シーケンス

```
1. Pagerオープン (sqlite3PagerOpen)
   └─ VFSを使用してDBファイルをオープン

2. 共有ロック取得 (sqlite3PagerSharedLock)
   └─ ホットジャーナルのチェック・ロールバック

3. ページ読み取り (sqlite3PagerGet)
   └─ キャッシュから取得、なければディスクから読み込み

4. 書き込みトランザクション開始 (sqlite3PagerBegin)
   └─ RESERVEDロックを取得

5. ページ書き込み (sqlite3PagerWrite)
   └─ 変更前のページをジャーナルへ記録

6. コミット Phase1 (sqlite3PagerCommitPhaseOne)
   └─ ジャーナルを同期、DBファイルへ書き込み

7. コミット Phase2 (sqlite3PagerCommitPhaseTwo)
   └─ ジャーナルファイルをファイナライズ
```

### フローチャート

```mermaid
flowchart TD
    A[OPEN状態] --> B{共有ロック要求}
    B -->|成功| C[READER状態]
    C --> D{書き込み要求}
    D -->|Yes| E[WRITER_LOCKED状態]
    E --> F[ジャーナルオープン]
    F --> G[WRITER_CACHEMOD状態]
    G --> H{DBファイル変更}
    H -->|Yes| I[WRITER_DBMOD状態]
    I --> J[WRITER_FINISHED状態]
    J --> K{コミット成功?}
    K -->|Yes| C
    K -->|No| L[ERROR状態]
    L --> A
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-21-01 | ジャーナル先行書き込み | ページを上書きする前に必ずジャーナルへ元データを記録 | ロールバックジャーナル使用時 |
| BR-21-02 | ロック昇格順序 | SHARED→RESERVED→EXCLUSIVE の順序でロックを昇格 | 書き込みトランザクション時 |
| BR-21-03 | セクター境界整合 | 書き込みはセクターサイズの整数倍で行う | atomic write非対応デバイス |

### 計算ロジック

**チェックサム計算（ジャーナル整合性検証用）**:
```c
// pager_cksum関数（2240-2248行目）
checksum = cksumInit;
for (i = pageSize - 200; i > 0; i -= 200) {
    checksum += aData[i];
}
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象ファイル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ページ読み取り | DBファイル | READ | ページデータの読み込み |
| ページ書き込み | ジャーナル | WRITE | 変更前データの記録 |
| コミット | DBファイル | WRITE | 変更後データの書き込み |
| ロールバック | DBファイル | WRITE | ジャーナルからデータ復元 |

### テーブル別操作詳細

#### ジャーナルファイル構造

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| WRITE | マジックナンバー | 0xd9d505f920a163d7 | ジャーナルヘッダ先頭8バイト |
| WRITE | ページ数 | トランザクション中の変更ページ数 | ヘッダ内 |
| WRITE | チェックサム初期値 | ランダム値 | 整合性検証用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SQLITE_BUSY | ロック競合 | 他プロセスがロック保持中 | リトライまたはタイムアウト |
| SQLITE_IOERR | I/Oエラー | ディスク書き込み失敗 | ERROR状態へ遷移 |
| SQLITE_FULL | ディスク容量不足 | 書き込み領域なし | ロールバック実行 |
| SQLITE_CORRUPT | データ破損 | ジャーナルチェックサム不一致 | 該当レコードをスキップ |

### リトライ仕様

busy_handlerコールバックが設定されている場合、SQLITE_BUSYエラー時にコールバックを呼び出し、戻り値が非ゼロならリトライする。

## トランザクション仕様

- トランザクション開始: sqlite3PagerBegin()でRESERVEDロック取得
- コミット: 2フェーズコミット（PhaseOne: ジャーナル同期→DB書き込み、PhaseTwo: ジャーナルファイナライズ）
- ロールバック: ジャーナルから元データを読み取りDBへ書き戻し

## パフォーマンス要件

- ページサイズ: 512〜65536バイト（デフォルト4096）
- キャッシュサイズ: PRAGMA cache_sizeで設定可能
- ジャーナルモード: DELETE/PERSIST/TRUNCATE/MEMORY/WAL/OFF

## セキュリティ考慮事項

- ジャーナルファイルには変更前のデータが平文で保存される
- 一時ファイルは自動削除されるが、ディスク上に残存する可能性あり
- exclusiveモードで他プロセスからのアクセスを完全に遮断可能

## 備考

- WALモードの場合、ページャーはWALサブシステムと連携して動作する
- メモリデータベース（:memory:）の場合、ファイルI/Oは発生しない

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、ページャーの状態管理と主要なデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | pager.h | `src/pager.h` | Pager型の宣言、ジャーナルモード定数、API関数宣言 |
| 1-2 | pager.c | `src/pager.c` | Pager構造体の定義（619-706行目）、状態定数（351-357行目） |

**読解のコツ**:
- ページャーの7つの状態（OPEN, READER, WRITER_LOCKED, WRITER_CACHEMOD, WRITER_DBMOD, WRITER_FINISHED, ERROR）と状態遷移図（139-154行目のASCII図）を理解することが重要
- PagerSavepoint構造体（431-442行目）はセーブポイント実装に必要

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pager.c | `src/pager.c` | sqlite3PagerOpen()関数 - ページャーの初期化 |

**主要処理フロー**:
1. **619-706行目**: Pager構造体のフィールド定義（状態変数、ファイルディスクリプタ等）
2. **351-357行目**: 状態定数の定義（PAGER_OPEN=0 〜 PAGER_ERROR=6）
3. **757-759行目**: ジャーナルマジックナンバーの定義

#### Step 3: トランザクション制御を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pager.c | `src/pager.c` | sqlite3PagerBegin() - 書き込みトランザクション開始 |
| 3-2 | pager.c | `src/pager.c` | sqlite3PagerCommitPhaseOne/Two() - コミット処理 |
| 3-3 | pager.c | `src/pager.c` | sqlite3PagerRollback() - ロールバック処理 |

**主要処理フロー**:
- **2041-2169行目**: pager_end_transaction() - トランザクション終了処理とジャーナルファイナライズ
- **2191-2219行目**: pagerUnlockAndRollback() - ロールバック実行
- **2240-2248行目**: pager_cksum() - ジャーナルレコードのチェックサム計算

#### Step 4: ページ読み書きを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pager.c | `src/pager.c` | sqlite3PagerGet() - ページ取得 |
| 4-2 | pager.c | `src/pager.c` | sqlite3PagerWrite() - ページ書き込み |
| 4-3 | pager.c | `src/pager.c` | pager_playback_one_page() - ロールバック時のページ復元 |

**主要処理フロー**:
- **2287-2299行目**: pager_playback_one_page() - ジャーナルからのページ読み取りと復元

### プログラム呼び出し階層図

```
sqlite3PagerOpen()
    │
    ├─ sqlite3PagerSetPagesize()
    │
    └─ sqlite3PcacheOpen()
           └─ [ページキャッシュ初期化]

sqlite3PagerGet()
    │
    ├─ sqlite3PcacheFetch()
    │      └─ [キャッシュから検索]
    │
    └─ pagerReadDbPage()
           └─ sqlite3OsRead()
                  └─ [VFS経由でファイル読み取り]

sqlite3PagerCommitPhaseOne()
    │
    ├─ syncJournal()
    │      └─ sqlite3OsSync()
    │
    └─ pager_write_pagelist()
           └─ sqlite3OsWrite()
```

### データフロー図

```
[入力]                    [処理]                         [出力]

B-Tree要求 ──────▶ sqlite3PagerGet() ─────────▶ DbPage*
                         │
                         ├─ キャッシュヒット ──▶ 即座に返却
                         │
                         └─ キャッシュミス
                                │
                    VFS読み取り ◀┘
                         │
                    ページキャッシュ登録

ページ変更 ──────▶ sqlite3PagerWrite() ───────▶ ジャーナル記録
                         │
              元データをジャーナルへ
                         │
              ダーティフラグ設定

コミット要求 ────▶ sqlite3PagerCommitPhaseOne()
                         │
              ジャーナル同期
                         │
              DBファイル書き込み ──────────────▶ 永続化完了
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| pager.c | `src/pager.c` | ソース | ページャーの主実装 |
| pager.h | `src/pager.h` | ヘッダ | 公開API定義 |
| pcache.h | `src/pcache.h` | ヘッダ | ページキャッシュインターフェース |
| pcache1.c | `src/pcache1.c` | ソース | デフォルトページキャッシュ実装 |
| wal.c | `src/wal.c` | ソース | WALモード実装（ページャーと連携） |
| wal.h | `src/wal.h` | ヘッダ | WALインターフェース |
