# 機能設計書 12-CREATE INDEX処理

## 概要

本ドキュメントは、SQLiteにおけるCREATE INDEX文の処理機能について記述する。CREATE INDEXは、テーブルの列に対してB-Treeインデックスを作成し、クエリの検索性能を向上させる機能である。

### 本機能の処理概要

**業務上の目的・背景**：データベースにおいてWHERE句やJOIN条件で頻繁に使用される列には、インデックスを作成することで検索性能を大幅に向上できる。特に大量データを扱うアプリケーションでは、適切なインデックス設計がシステム性能の鍵となる。CREATE INDEX機能は、明示的なインデックス作成を可能にし、クエリオプティマイザがより効率的な実行計画を選択できるようにする。

**機能の利用シーン**：
- 検索条件で頻繁に使用される列へのインデックス作成
- UNIQUE制約の実装（重複チェック用インデックス）
- 複合インデックス（複数列の組み合わせ）の作成
- 部分インデックス（WHERE句付きインデックス）の作成
- 式インデックス（計算式に基づくインデックス）の作成

**主要な処理内容**：
1. SQL文の解析とインデックス名の検証
2. 対象テーブルの存在確認と検証
3. インデックス列リストの解析と照合順序の決定
4. 認証コールバックによるアクセス権限チェック
5. Index構造体の割り当てと初期化
6. B-Treeルートページの作成
7. sqlite_schemaテーブルへのメタデータ登録
8. 既存データからのインデックス構築（REINDEX相当処理）
9. スキーマ変更の確定（スキーマクッキーの更新）

**関連システム・外部連携**：VDBEバイトコードエンジン、B-Treeストレージエンジン、クエリオプティマイザ

**権限による制御**：sqlite3_set_authorizer()で設定された認可コールバックにより、SQLITE_CREATE_INDEX、SQLITE_CREATE_TEMP_INDEXのアクションコードでアクセス制御が可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 直接的な画面関連なし（バックエンド処理） |

## 機能種別

DDL操作（データ定義言語） / スキーマ変更 / インデックス管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pName1 | Token* | No | インデックス名の第1部分（データベース名） | 省略可能 |
| pName2 | Token* | No | インデックス名の第2部分（インデックス名） | 省略可能 |
| pTblName | SrcList* | No | インデックス対象テーブル名 | NULLの場合はpParse->pNewTable使用 |
| pList | ExprList* | No | インデックス列リスト | NULLの場合は最後の列を使用 |
| onError | int | Yes | 重複時動作（OE_Abort, OE_Ignore, OE_Replace, OE_None） | UNIQUE制約のエラー処理 |
| pStart | Token* | No | CREATE文の開始トークン | CREATE INDEX文の記録用 |
| pPIWhere | Expr* | No | 部分インデックスのWHERE句 | 条件式の検証 |
| sortOrder | int | Yes | PRIMARY KEY時のソート順序 | pList==NULL時に使用 |
| ifNotExist | int | Yes | IF NOT EXISTS指定フラグ | 1で既存時エラー抑制 |
| idxType | u8 | Yes | インデックス種別 | SQLITE_IDXTYPE_APPDEF/PRIMARYKEY/UNIQUE |

### 入力データソース

- SQL文解析結果（パーサーから渡されるトークン情報）
- データベーススキーマ（sqlite_schemaテーブル）
- 対象テーブルの既存データ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | void | 直接的な戻り値なし |
| pParse->nErr | int | エラー発生時にインクリメント |
| pParse->pNewIndex | Index* | 新規作成されたIndexオブジェクト |

### 出力先

- sqlite_schemaテーブル（インデックスメタデータ）
- B-Treeストレージ（インデックスデータページ）
- メモリ内スキーマ（Index構造体）

## 処理フロー

### 処理シーケンス

```
1. エラーチェック
   └─ pParse->nErr, IN_DECLARE_VTAB, スキーマ読み込み

2. NULLS FIRST/LASTチェック
   └─ sqlite3HasExplicitNulls()でサポート外構文を検出

3. テーブル検索
   └─ pTblName指定時: sqlite3LocateTableItem()
   └─ 未指定時: pParse->pNewTableを使用

4. テーブル検証
   └─ sqlite_*テーブル、VIEW、VIRTUALテーブルは不可

5. インデックス名決定
   └─ 明示指定: pNameから取得
   └─ 自動生成: sqlite_autoindex_テーブル名_番号

6. 名前重複チェック
   └─ 同名テーブル/インデックスの存在確認

7. 認証チェック
   └─ SQLITE_INSERT (sqlite_schema), SQLITE_CREATE_INDEX

8. インデックス列リスト処理
   └─ 各列の照合順序、ソート順序を決定

9. Index構造体割り当て
   └─ sqlite3AllocateIndexObject()

10. 列情報設定
    └─ aiColumn[], azColl[], aSortOrder[]の設定

11. WITHOUT ROWIDテーブル処理
    └─ PRIMARY KEY列の追加

12. 重複インデックスチェック
    └─ 同等のインデックスが既存なら作成スキップ

13. B-Treeルートページ作成
    └─ OP_CreateBtree命令

14. sqlite_schema登録
    └─ INSERT INTO sqlite_schema

15. インデックス構築
    └─ sqlite3RefillIndex()で既存データからインデックス作成

16. スキーマ更新
    └─ sqlite3ChangeCookie()
```

### フローチャート

```mermaid
flowchart TD
    A[CREATE INDEX開始] --> B{スキーマ読み込み成功?}
    B -->|No| Z[終了]
    B -->|Yes| C{NULLS FIRST/LAST使用?}
    C -->|Yes| D[エラー: unsupported]
    D --> Z
    C -->|No| E[テーブル検索]
    E --> F{テーブル存在?}
    F -->|No| Z
    F -->|Yes| G{VIEW/VIRTUAL/sqlite_*?}
    G -->|Yes| H[エラー: may not be indexed]
    H --> Z
    G -->|No| I[インデックス名決定]
    I --> J{名前重複?}
    J -->|Yes| K{IF NOT EXISTS?}
    K -->|Yes| Z
    K -->|No| L[エラー: already exists]
    L --> Z
    J -->|No| M[認証チェック]
    M --> N{認証成功?}
    N -->|No| Z
    N -->|Yes| O[Index構造体作成]
    O --> P[列情報設定]
    P --> Q{同等インデックス存在?}
    Q -->|Yes| Z
    Q -->|No| R[B-Treeルートページ作成]
    R --> S[sqlite_schema登録]
    S --> T[インデックス構築]
    T --> U[スキーマクッキー更新]
    U --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-12-01 | システムテーブル禁止 | sqlite_*テーブルにはインデックス作成不可 | 常時（init.busy時を除く） |
| BR-12-02 | VIEW禁止 | VIEWにはインデックス作成不可 | 常時 |
| BR-12-03 | 仮想テーブル禁止 | VIRTUAL TABLEにはインデックス作成不可 | 常時 |
| BR-12-04 | TEMP不整合禁止 | 非TEMPテーブルにTEMPインデックス作成不可 | 常時 |
| BR-12-05 | 自動インデックス名 | 名前未指定時はsqlite_autoindex_テーブル名_N | PRIMARY KEY/UNIQUE制約時 |
| BR-12-06 | 重複インデックス抑制 | 同一列構成のインデックスは重複作成しない | PRIMARY KEY/UNIQUE制約時 |
| BR-12-07 | カバリングインデックス | 全列を含むインデックスはisCovering=1 | 常時 |

### 計算ロジック

**インデックス行推定（sqlite3DefaultRowEst）**:
- 最初の列: 10行あたり1つのユニーク値
- 後続列: 前列の1/10のユニーク値
- UNIQUEインデックス: 最終列は0（1行のみ）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メタデータ登録 | sqlite_schema | INSERT | インデックス定義を登録 |
| インデックス構築 | 新規インデックス | データ追加 | 既存行からインデックスエントリ作成 |

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

#### sqlite_schema

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | type | 'index' | 固定値 |
| INSERT | name | インデックス名 | 自動生成または指定 |
| INSERT | tbl_name | テーブル名 | 対象テーブル |
| INSERT | rootpage | B-Treeルートページ番号 | 動的割り当て |
| INSERT | sql | CREATE INDEX文 | 自動インデックスはNULL |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | unsupported use of NULLS FIRST/LAST | NULLS句使用 | NULLS句を削除 |
| - | table %s may not be indexed | システムテーブル等 | 対象テーブルを変更 |
| - | views may not be indexed | VIEW指定 | テーブルを指定 |
| - | virtual tables may not be indexed | VIRTUAL TABLE指定 | 仮想テーブル以外を指定 |
| - | there is already a table named %s | 同名テーブル存在 | 別名を使用 |
| - | index %s already exists | 同名インデックス存在 | IF NOT EXISTS使用または別名 |
| - | cannot create a TEMP index on non-TEMP table | TEMP不整合 | 対象テーブルを確認 |
| - | conflicting ON CONFLICT clauses | ON CONFLICT競合 | 一方を削除 |
| SQLITE_AUTH | 認証エラー | authorizerが拒否 | 権限を確認 |

### リトライ仕様

CREATE INDEXはアトミック操作であり、失敗時は自動ロールバックされる。明示的なリトライ機構は提供されない。

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

- 書き込みトランザクション内で実行される
- sqlite3BeginWriteOperation()により排他ロックを取得
- 大量データのインデックス構築時もアトミックに処理
- スキーマクッキーが更新され、他の接続のキャッシュが無効化される

## パフォーマンス要件

- インデックス構築時間はテーブル行数に比例
- 大規模テーブルでのCREATE INDEXは長時間実行となる可能性
- sqlite3RefillIndex()がソート処理を伴うため、メモリ使用量に注意

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

- sqlite3_set_authorizer()コールバックによるアクセス制御
- SQLITE_CREATE_INDEX / SQLITE_CREATE_TEMP_INDEX アクションでの認可
- sqlite_schemaへのINSERT権限チェック

## 備考

- 式インデックス（expression index）: 列名の代わりに式を指定可能
- 部分インデックス（partial index）: WHERE句で対象行を限定可能
- WITHOUT ROWIDテーブルではPRIMARY KEY列がインデックス末尾に追加される

---

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

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

### 推奨読解順序

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

まず、インデックス管理で使用される主要なデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sqliteInt.h | `src/sqliteInt.h` | Index構造体の定義（nKeyCol, aiColumn, azColl, aSortOrder等） |
| 1-2 | sqliteInt.h | `src/sqliteInt.h` | ExprList構造体（インデックス列リスト） |

**読解のコツ**: Index構造体のaiColumn配列は列インデックスを、azCollは照合順序名を、aSortOrderはASC/DESC情報を保持する。XN_EXPR(-2)は式インデックスを示す特殊値。

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

処理の起点となる関数を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | build.c | `src/build.c` | sqlite3CreateIndex()関数（3936行目〜）がエントリーポイント |

**主要処理フロー**:
1. **3967-3979行目**: エラーチェック、DECLARE VTAB内チェック、スキーマ読み込み
2. **3977行目**: sqlite3HasExplicitNulls()でNULLS句チェック
3. **3984-4030行目**: テーブル検索とデータベース決定
4. **4034-4051行目**: sqlite_*/VIEW/VIRTUALテーブルチェック
5. **4067-4107行目**: インデックス名の決定（明示または自動生成）
6. **4111-4122行目**: 認証チェック
7. **4129-4142行目**: 列リスト処理（NULLの場合は最後の列）
8. **4159-4182行目**: Index構造体の割り当てと初期化
9. **4207-4267行目**: 各列の情報設定（照合順序、ソート順序）
10. **4310-4374行目**: 既存インデックスとの重複チェック
11. **4419-4476行目**: B-Tree作成、sqlite_schema登録、インデックス構築

#### Step 3: インデックス構築処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | build.c | `src/build.c` | sqlite3RefillIndex()関数 |

**主要処理フロー**:
- テーブルの全行をスキャン
- 各行からインデックスキーを生成
- B-Treeにキーを挿入

#### Step 4: 行推定ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | build.c | `src/build.c` | sqlite3DefaultRowEst()関数 |

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

```
sqlite3CreateIndex() [build.c:3936]
    │
    ├─ sqlite3ReadSchema() [prepare.c]
    │
    ├─ sqlite3HasExplicitNulls() [build.c:3908]
    │
    ├─ sqlite3TwoPartName() [build.c]
    │      └─ データベース名とインデックス名の分離
    │
    ├─ sqlite3LocateTableItem() [build.c:481]
    │      └─ テーブル検索
    │
    ├─ sqlite3AuthCheck() [auth.c]
    │      └─ SQLITE_CREATE_INDEX認証
    │
    ├─ sqlite3AllocateIndexObject() [build.c]
    │      └─ Index構造体のメモリ割り当て
    │
    ├─ sqlite3ResolveSelfReference() [resolve.c]
    │      └─ 式インデックスの解決
    │
    ├─ sqlite3DefaultRowEst() [build.c]
    │      └─ 行推定値の設定
    │
    ├─ sqlite3BeginWriteOperation() [build.c]
    │
    ├─ sqlite3NestedParse() [build.c:293]
    │      └─ INSERT INTO sqlite_schema
    │
    ├─ sqlite3RefillIndex() [build.c]
    │      └─ 既存データからインデックス構築
    │
    └─ sqlite3ChangeCookie() [build.c]
```

### データフロー図

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

CREATE INDEX文 ─────▶ パーサー ─────────────▶ Parse構造体
                           │
                           ▼
                  sqlite3CreateIndex()
                           │
          ┌────────────────┼────────────────┐
          ▼                ▼                ▼
    テーブル検索      名前検証         認証チェック
          │                │                │
          └────────────────┼────────────────┘
                           ▼
                  Index構造体作成
                           │
          ┌────────────────┼────────────────┐
          ▼                ▼                ▼
    aiColumn[]        azColl[]       aSortOrder[]
    (列インデックス)   (照合順序)      (ソート順)
          │                │                │
          └────────────────┼────────────────┘
                           ▼
                  B-Tree作成
                           │
                           ▼
               sqlite_schema ──────▶ メタデータ登録
                           │
                           ▼
               sqlite3RefillIndex()
                           │
               テーブル全行 ──────▶ インデックスエントリ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| build.c | `src/build.c` | ソース | CREATE INDEX主要処理、sqlite3CreateIndex() |
| resolve.c | `src/resolve.c` | ソース | 式インデックスの名前解決 |
| auth.c | `src/auth.c` | ソース | 認証処理 |
| vdbeaux.c | `src/vdbeaux.c` | ソース | VDBEヘルパー関数 |
| btree.c | `src/btree.c` | ソース | B-Tree操作（ルートページ作成） |
| sqliteInt.h | `src/sqliteInt.h` | ヘッダー | Index構造体定義 |
| vdbe.h | `src/vdbe.h` | ヘッダー | VDBE命令定義（OP_CreateBtree等） |
