# 通知設計書 3-sqlite3ConnectionUnlocked

## 概要

本ドキュメントは、SQLiteの内部関数`sqlite3ConnectionUnlocked()`の設計について記載する。この関数は、トランザクション終了時に登録済みのunlock-notifyコールバックを呼び出す機能を提供し、`sqlite3_unlock_notify()` APIの中核となる通知発火処理を担う。

### 本通知の処理概要

`sqlite3ConnectionUnlocked()`は、データベース接続がトランザクションを終了（コミットまたはロールバック）した際に呼び出される内部関数である。この関数は、ブロックリスト内の接続をスキャンし、終了したトランザクションを待機していた接続に対して登録済みのコールバックを呼び出す。

**業務上の目的・背景**：`sqlite3_unlock_notify()`でコールバックを登録した接続は、ブロッキング接続のトランザクション終了を待機している。トランザクション終了時に待機中の接続に通知を送ることで、アプリケーションは失敗したSQL操作を再試行できる。この関数がなければ、登録されたコールバックは永久に呼び出されず、アプリケーションは無期限に待機することになる。

**通知の送信タイミング**：`sqlite3_step()`実行後にautoCommitフラグがtrueの場合（自動コミット時）、VDBEエンジンから呼び出される。具体的には、`vdbeaux.c`の`sqlite3VdbeResetStepResult()`関連処理内で、トランザクションが完了した際に実行される。

**通知の受信者**：`sqlite3_unlock_notify()`で登録されたコールバック関数。同一のコールバック関数を登録した複数の接続がある場合、それらのコンテキストポインタは配列としてまとめられ、一度のコールバック呼び出しで効率的に通知される。

**通知内容の概要**：コールバック関数には、登録時に指定したコンテキストポインタの配列（`apArg`）とその要素数（`nArg`）が渡される。配列には、同一コールバックを登録していたすべての待機中接続のコンテキストが含まれる。

**期待されるアクション**：コールバックを受け取ったアプリケーションは、以前SQLITE_LOCKEDで失敗したSQL操作を再試行する。ただし、コールバック内からsqlite3_xxx API関数を呼び出すことは禁止されている。

## 通知種別

コールバック関数呼び出し（C言語関数ポインタによるコールバック）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（トランザクション終了時に直接呼び出し） |
| 優先度 | 高（待機中の接続への即座の通知が必要） |
| リトライ | なし（コールバックは条件を満たした時点で即座に呼び出し） |

### 送信先決定ロジック

1. トランザクション終了時に`sqlite3BlockedList`を先頭からスキャン
2. 各エントリに対して以下を実行：
   - `pBlockingConnection`が終了した接続と一致する場合、NULLにクリア
   - `pUnlockConnection`が終了した接続と一致する場合、コールバック呼び出しの準備
3. 同一コールバック関数を持つエントリはコンテキストポインタを配列にまとめる
4. 異なるコールバック関数に切り替わる際、または最後に、コールバックを実行
5. `pBlockingConnection`と`pUnlockConnection`が両方NULLになったエントリはリストから削除

## 通知テンプレート

### 関数シグネチャ

```c
void sqlite3ConnectionUnlocked(sqlite3 *db)
```

| 引数 | 説明 |
|-----|------|
| db | トランザクションを終了した接続 |

### コールバック呼び出し形式

```c
void (*xUnlockNotify)(void **apArg, int nArg)
```

| 引数 | 説明 |
|-----|------|
| apArg | コンテキストポインタの配列 |
| nArg | 配列の要素数 |

### 添付ファイル

該当なし（コールバック関数呼び出し）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| apArg | コンテキストポインタ配列 | sqlite3.pUnlockArg の集約 | Yes |
| nArg | 配列要素数 | 該当するエントリ数 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | sqlite3_step()完了後 | db->autoCommit==true | 自動コミットトランザクション終了時 |
| 内部呼び出し | sqlite3ConnectionClosed() | 常に | 接続クローズ時（内部的に呼び出し） |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| autoCommit==false | 明示的トランザクション中は呼び出されない |
| sqlite3BlockedListが空 | 待機中の接続がない場合は何もしない |
| pUnlockConnection!=db | 終了した接続を待機していない場合はスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[トランザクション終了] --> B[sqlite3ConnectionUnlocked呼び出し]
    B --> C[enterMutex - STATIC_MAINロック取得]
    C --> D[sqlite3BlockedListをスキャン]
    D --> E{エントリあり?}
    E -->|No| F[leaveMutex - ロック解放]
    E -->|Yes| G{pBlockingConnection==db?}
    G -->|Yes| H[pBlockingConnection=NULL]
    G -->|No| I[スキップ]
    H --> J{pUnlockConnection==db?}
    I --> J
    J -->|Yes| K[コールバック準備]
    J -->|No| L[次エントリへ]
    K --> M{同一コールバック?}
    M -->|Yes| N[配列にコンテキスト追加]
    M -->|No| O[既存コールバック呼び出し]
    O --> N
    N --> P[pUnlockConnection等クリア]
    P --> Q{両方NULL?}
    Q -->|Yes| R[リストから削除]
    Q -->|No| S[リストに保持]
    R --> L
    S --> L
    L --> E
    F --> T[最終コールバック呼び出し]
    T --> U[メモリ解放]
    U --> V[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（インメモリデータ構造のみ使用）

### グローバル変数参照・更新

| 変数名 | 型 | 操作 | 用途 | 備考 |
|--------|---|------|------|------|
| sqlite3BlockedList | sqlite3* | 参照/更新 | ブロックされた接続のリンクリスト先頭 | エントリ削除時に更新 |

### sqlite3構造体メンバー参照・更新

| メンバー名 | 型 | 操作 | 用途 |
|-----------|---|------|------|
| pBlockingConnection | sqlite3* | 参照/更新 | NULLにクリア |
| pUnlockConnection | sqlite3* | 参照/更新 | NULLにクリア |
| xUnlockNotify | void(*)(void**,int) | 参照/更新 | コールバック呼び出し後にNULLにクリア |
| pUnlockArg | void* | 参照/更新 | 配列に追加後にNULLにクリア |
| pNextBlocked | sqlite3* | 参照/更新 | リスト操作時に更新 |

### 更新テーブル一覧

該当なし（インメモリデータ構造のみ使用）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| メモリ不足 | aArg配列拡張時のmalloc失敗 | 既存配列でコールバックを先に呼び出し、処理継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

### メモリ不足時の特殊処理

メモリ不足でaArg配列を拡張できない場合、既存の配列でコールバックを先に呼び出し、配列をリセットして処理を継続する。これにより、一部のコールバックが複数回に分割されて呼び出される可能性があるが、アプリケーションが無期限に待機することは防止される。

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（トランザクション終了時に即座に呼び出し）

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

- SQLITE_MUTEX_STATIC_MAINミューテックスでグローバルデータ構造を保護
- コールバック呼び出しはミューテックス保持中に実行される
- メモリ割り当ては`sqlite3BeginBenignMalloc()`で保護（OOMエラーでもパニックしない）

## 備考

- SQLITE_ENABLE_UNLOCK_NOTIFYコンパイルオプションが必要
- notify.cで実装され、vdbeaux.cから呼び出される
- 初期配列`aStatic[16]`を使用し、それを超える場合のみ動的割り当て
- `sqlite3ConnectionClosed()`からも内部的に呼び出される

---

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

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

### 推奨読解順序

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

まず、コールバック呼び出しに使用されるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sqliteInt.h | `src/sqliteInt.h` | sqlite3構造体のunlock-notify関連メンバー（1790-1803行目） |

**読解のコツ**: `pUnlockConnection`（待機対象接続）、`xUnlockNotify`（コールバック関数）、`pUnlockArg`（コールバック引数）が主要なメンバー。

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

処理の起点となる内部関数を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | notify.c | `src/notify.c` | sqlite3ConnectionUnlocked()関数（229-322行目）の実装 |

**主要処理フロー**:
1. **230-235行**: ローカル変数の初期化（aStatic[16]の固定配列）
2. **237-238行**: aArg = aStatic で初期配列設定、ミューテックス取得
3. **241-315行**: sqlite3BlockedListのスキャンループ
4. **245-247行**: Step 1 - pBlockingConnectionのクリア
5. **250-305行**: Step 2 - pUnlockConnectionの処理とコールバック準備
6. **252-255行**: 異なるコールバック関数への切り替え時に先行呼び出し
7. **260-297行**: aArg配列の動的拡張（メモリ不足対応含む）
8. **300-304行**: コンテキスト追加と状態クリア
9. **308-314行**: Step 3 - 両方NULLの場合はリストから削除
10. **317-318行**: 最終的なコールバック呼び出し
11. **320-321行**: メモリ解放とミューテックス解放

#### Step 3: 呼び出し元を理解する

この関数がどこから呼び出されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vdbeaux.c | `src/vdbeaux.c` | autoCommit時の呼び出し（3504-3508行目） |
| 3-2 | notify.c | `src/notify.c` | sqlite3ConnectionClosed()からの内部呼び出し（329行目） |

**主要処理フロー（vdbeaux.c:3507-3508行目）**:
```c
if( db->autoCommit ){
  sqlite3ConnectionUnlocked(db);
}
```
- 自動コミットモードでトランザクションが終了した場合に呼び出し

#### Step 4: メモリ管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | notify.c | `src/notify.c` | aStatic配列とaDyn動的配列の使い分け（234-235, 260-297行目） |

**読解のコツ**:
- 初期は`aStatic[16]`固定配列を使用
- 16要素を超える場合は`sqlite3Malloc()`で動的配列を確保
- メモリ不足時は既存配列でコールバックを先に呼び出し、処理継続

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

```
vdbeaux.c: sqlite3VdbeResetStepResult() または関連処理
    |
    +-- [autoCommit==true の場合]
    |
    +-- sqlite3ConnectionUnlocked(db)  [notify.c:229]
            |
            +-- enterMutex()  [notify.c:238]
            |
            +-- [sqlite3BlockedListスキャンループ]
            |       |
            |       +-- [pBlockingConnection==dbの場合] → NULLにクリア
            |       |
            |       +-- [pUnlockConnection==dbの場合]
            |               |
            |               +-- [異なるxUnlockNotifyの場合] → 先行コールバック呼び出し
            |               |
            |               +-- [aArg配列拡張が必要な場合]
            |               |       +-- sqlite3Malloc()
            |               |       +-- [失敗時] → 既存配列でコールバック呼び出し
            |               |
            |               +-- aArg[nArg++] = pUnlockArg
            |               +-- 状態クリア
            |
            +-- xUnlockNotify(aArg, nArg)  [最終コールバック]
            |
            +-- sqlite3_free(aDyn)
            |
            +-- leaveMutex()
```

### データフロー図

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

sqlite3 *db          --->  sqlite3ConnectionUnlocked()
(トランザクション終了)           |
                               v
                         [ミューテックス取得]
                               |
                               v
                         [sqlite3BlockedListスキャン]
                               |
                               +--- [pBlockingConnection==db] ---> NULLクリア
                               |
                               +--- [pUnlockConnection==db]
                                       |
                                       v
                               [コンテキスト収集]
                                       |
                                       v
                               [xUnlockNotify呼び出し] ---> アプリケーションコールバック
                                       |
                                       v
                               [リストからエントリ削除]
                                       |
                                       v
                               [ミューテックス解放]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notify.c | `src/notify.c` | ソース | sqlite3ConnectionUnlocked()の実装 |
| sqliteInt.h | `src/sqliteInt.h` | ヘッダー | sqlite3構造体定義、関数プロトタイプ |
| vdbeaux.c | `src/vdbeaux.c` | ソース | 呼び出し元（トランザクション終了時） |
| malloc.c | `src/malloc.c` | ソース | sqlite3Malloc(), sqlite3BeginBenignMalloc()の実装 |
