# 機能設計書 25-ATTACH/DETACH

## 概要

本ドキュメントは、SQLiteのATTACH/DETACHコマンドの機能設計を記述する。これらのコマンドにより、単一のデータベース接続に複数のデータベースファイルを接続し、クロスデータベースクエリを実行できる。

### 本機能の処理概要

ATTACH DATABASEは外部データベースファイルを現在の接続に追加し、エイリアス名でアクセスできるようにする。DETACH DATABASEは接続済みのデータベースを切り離す。

**業務上の目的・背景**：複数のデータベースファイルに分散したデータを単一のSQL文で操作したい場合がある。例えば、メインデータベースと履歴データベースを分離しつつ、結合クエリで分析を行うケースなど。ATTACHを使用することで、アプリケーション側でデータを統合することなく、SQL層で複数データベースを透過的に操作できる。

**機能の利用シーン**：
- バックアップデータベースの参照
- データ移行時のソース・ターゲット同時アクセス
- シャーディングされたデータの統合クエリ
- 一時データベースの利用

**主要な処理内容**：
1. ATTACH: 新しいデータベースファイルのオープンとスキーマ読み込み
2. DETACH: データベースのクローズとリソース解放
3. スキーマ名修飾によるテーブルアクセス（schema.table形式）

**関連システム・外部連携**：B-Treeを通じてデータベースファイルにアクセス。パーサーでスキーマ名解決を行う。

**権限による制御**：接続フラグSQLITE_AttachWrite/SQLITE_AttachCreateで書き込み・作成権限を制御

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | CLIメイン | 主機能 | .databasesコマンドで接続一覧表示 |

## 機能種別

データベース管理 / SQL DDL

## 入力仕様

### 入力パラメータ（ATTACH）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filename | TEXT | Yes | データベースファイルパス | 有効なパスまたは':memory:' |
| database_name | TEXT | Yes | エイリアス名 | 既存名と重複不可 |
| key | TEXT | No | 暗号化キー（SEE使用時） | NULL許容 |

### 入力パラメータ（DETACH）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| database_name | TEXT | Yes | デタッチするDB名 | main/temp以外 |

### 入力データソース

- SQL文によるATTACH DATABASE / DETACH DATABASE

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| rc | int | 操作結果（SQLITE_OK等） |

### 出力先

- 内部データベース接続配列（db->aDb）

## 処理フロー

### 処理シーケンス（ATTACH）

```
1. SQL解析
   └─ ATTACH DATABASE expr AS name [KEY expr] をパース

2. attachFunc呼び出し（SQL関数として実装）
   └─ 引数検証

3. データベース追加チェック
   └─ 最大接続数チェック
   └─ 名前重複チェック
   └─ トランザクション中でないことを確認

4. aDb配列拡張
   └─ 新しいDb構造体を追加

5. B-Treeオープン
   └─ sqlite3BtreeOpen()

6. スキーマ初期化
   └─ sqlite3Init()

7. エンコーディング確認
   └─ mainと同じtext encodingであること
```

### フローチャート

```mermaid
flowchart TD
    A[ATTACH DATABASE file AS name] --> B{接続数チェック}
    B -->|超過| C[エラー: too many attached databases]
    B -->|OK| D{名前重複チェック}
    D -->|重複| E[エラー: already in use]
    D -->|OK| F{トランザクション中?}
    F -->|Yes| G[エラー: cannot ATTACH during transaction]
    F -->|No| H[aDb配列拡張]
    H --> I[B-Treeオープン]
    I --> J{オープン成功?}
    J -->|No| K[エラー返却]
    J -->|Yes| L[スキーマ初期化]
    L --> M{エンコーディング一致?}
    M -->|No| N[エラー: encoding mismatch]
    M -->|Yes| O[ATTACH完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-25-01 | 最大接続数 | SQLITE_LIMIT_ATTACHEDまでアタッチ可能（デフォルト10） | ATTACH時 |
| BR-25-02 | 名前予約 | "main"と"temp"は予約済みでDETACH不可 | DETACH時 |
| BR-25-03 | エンコーディング一致 | アタッチDBはmainと同じtext encodingが必要 | ATTACH時 |
| BR-25-04 | トランザクション禁止 | トランザクション中はATTACH/DETACH不可 | 常時 |
| BR-25-05 | 読み取り専用制限 | SQLITE_AttachWriteなしの場合は読み取り専用 | ATTACH時 |
| BR-25-06 | 作成禁止制限 | SQLITE_AttachCreateなしの場合はDB作成不可 | ATTACH時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ATTACH | ターゲットDB | OPEN | ファイルオープンとスキーマ読み込み |
| DETACH | ターゲットDB | CLOSE | ファイルクローズ |

### 内部構造変更

| 操作 | 項目 | 更新内容 | 備考 |
|-----|-----------------|-----------------|------|
| ATTACH | db->aDb[] | 新エントリ追加 | Db構造体 |
| ATTACH | db->nDb | インクリメント | データベース数 |
| DETACH | db->aDb[n] | エントリ削除 | B-Treeクローズ |
| DETACH | db->nDb | デクリメント | データベース数 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SQLITE_ERROR | 制限超過 | 最大接続数超過 | DETACH後に再試行 |
| SQLITE_ERROR | 名前重複 | 同名DBが既にアタッチ済み | 別名を使用 |
| SQLITE_ERROR | エンコーディング不一致 | mainと異なるtext encoding | 同一encodingのDBを使用 |
| SQLITE_ERROR | デタッチ不可 | mainまたはtempをデタッチ試行 | 予約名は使用不可 |
| SQLITE_ERROR | トランザクション中 | トランザクション実行中 | COMMITまたはROLLBACK後に実行 |
| SQLITE_CANTOPEN | オープン失敗 | ファイルが存在しない/アクセス権なし | パスとアクセス権を確認 |

### リトライ仕様

エラー発生時の自動リトライはなし。アプリケーションで適切な対処後に再試行が必要。

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

- ATTACH/DETACHはトランザクション外でのみ実行可能
- アタッチされたDBは独立したトランザクションを持つ
- 複数DBにまたがるトランザクションでは、各DBのジャーナルにスーパージャーナルへの参照が記録される

## パフォーマンス要件

- ATTACH時にスキーマ全体を読み込むため、大規模スキーマでは時間がかかる可能性あり
- アタッチ後のクエリは通常のクエリと同等のパフォーマンス

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

- authorizer関数でATTACH/DETACHを拒否可能
- SQLITE_AttachWrite/SQLITE_AttachCreateフラグで書き込み・作成を制限可能
- ファイルパスはVFS経由でアクセスされるため、VFSのセキュリティ制限が適用される

## 備考

- SQLITE_OMIT_ATTACHでコンパイル時に無効化可能
- デフォルトの最大接続数は10（SQLITE_MAX_ATTACHEDで変更可能）
- :memory:データベースもATTACH可能

---

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

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

### 推奨読解順序

#### Step 1: SQL関数実装を理解する

ATTACH/DETACHはSQL関数として実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | attach.c | `src/attach.c` | attachFunc()関数（74-274行目） |
| 1-2 | attach.c | `src/attach.c` | detachFunc()関数（284-300行目以降） |

**読解のコツ**:
- SQL文 `ATTACH DATABASE x AS y KEY z` は内部的に `SELECT sqlite_attach(x, y, z)` に変換される
- attachFuncはsqlite3_context経由でエラーを返す

#### Step 2: ATTACH処理フローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | attach.c | `src/attach.c` | 制限チェック（137-149行目） |
| 2-2 | attach.c | `src/attach.c` | aDb配列拡張（154-164行目） |
| 2-3 | attach.c | `src/attach.c` | B-Treeオープン（186行目） |
| 2-4 | attach.c | `src/attach.c` | スキーマ初期化（238行目） |

**主要処理フロー**:
1. **137-142行目**: 最大接続数チェック（db->aLimit[SQLITE_LIMIT_ATTACHED]+2）
2. **143-149行目**: 名前重複チェック（sqlite3DbIsNamed()）
3. **154-164行目**: aDb配列のメモリ拡張
4. **178-183行目**: 読み取り専用/作成禁止フラグの適用
5. **186行目**: sqlite3BtreeOpen()でB-Treeオープン
6. **196-203行目**: スキーマ取得とエンコーディングチェック
7. **238行目**: sqlite3Init()でスキーマ初期化

#### Step 3: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | attach.c | `src/attach.c` | resolveAttachExpr()（35-46行目） |
| 3-2 | attach.c | `src/attach.c` | sqlite3DbIsNamed()（52-57行目） |

**主要処理フロー**:
- **35-46行目**: ATTACH式の解決（単純な識別子は文字列リテラルとして扱う）
- **52-57行目**: データベース名の照合（"main"も"main"データベースを指す）

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

```
SQL: ATTACH DATABASE 'file.db' AS alias
    │
    └─ パーサー変換
           │
           ▼
    SQL: SELECT sqlite_attach('file.db', 'alias', NULL)
           │
           └─ attachFunc()
                  │
                  ├─ 制限チェック
                  │      ├─ 最大接続数
                  │      └─ 名前重複
                  │
                  ├─ aDb配列拡張
                  │
                  ├─ sqlite3BtreeOpen()
                  │      └─ ファイルオープン
                  │
                  ├─ sqlite3SchemaGet()
                  │      └─ スキーマ構造体取得
                  │
                  └─ sqlite3Init()
                         └─ スキーマ読み込み

SQL: DETACH DATABASE alias
    │
    └─ パーサー変換
           │
           ▼
    SQL: SELECT sqlite_detach('alias')
           │
           └─ detachFunc()
                  │
                  ├─ 名前検索
                  │
                  ├─ main/tempチェック
                  │
                  ├─ 使用中チェック
                  │
                  └─ sqlite3BtreeClose()
                         └─ ファイルクローズ
```

### データフロー図

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

ファイルパス ────────▶ attachFunc()
エイリアス名                │
暗号キー                   │
                   制限・検証チェック
                           │
                   aDb配列拡張
                           │
                   sqlite3BtreeOpen()
                           │
                   sqlite3Init()
                           │
                           ▼
                   db->aDb[]更新 ────────▶ アタッチ完了

エイリアス名 ────────▶ detachFunc()
                           │
                   名前検索
                           │
                   使用中チェック
                           │
                   sqlite3BtreeClose()
                           │
                           ▼
                   db->aDb[]更新 ────────▶ デタッチ完了
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| attach.c | `src/attach.c` | ソース | ATTACH/DETACH主実装 |
| build.c | `src/build.c` | ソース | スキーマ操作 |
| btree.c | `src/btree.c` | ソース | B-Treeオープン/クローズ |
| prepare.c | `src/prepare.c` | ソース | sqlite3Init() |
| sqliteInt.h | `src/sqliteInt.h` | ヘッダ | Db構造体定義 |
