# 機能設計書 68-SQLクエリ

## 概要

本ドキュメントは、NushellにおけるSQLクエリ実行機能（query db, schema, into sqlite）の設計を記述する。SQLite データベースに対して SQL クエリを実行し、結果を Nushell の値として取得する。

### 本機能の処理概要

SQLクエリ機能は、SQLite データベースに対して SQL 文を実行し、結果を Nushell のテーブルやレコードとして返す。パラメータ化クエリ、スキーマ取得、データベースへの変換をサポートする。

**業務上の目的・背景**：SQLite データベースからデータを取得・操作する際に、SQL の柔軟性を活かしつつ、結果を Nushell のパイプラインで処理したい場面がある。本機能により、既存の SQLite データベースとの連携が可能となる。

**機能の利用シーン**：
- SQLite データベースからのデータ抽出
- 複雑なクエリによるデータ分析
- データベーススキーマの確認
- Nushell テーブルの SQLite への変換
- パラメータ化クエリによる安全なデータ操作

**主要な処理内容**：
1. `query db` - SQL クエリを実行
2. `schema` - データベーススキーマを表示
3. `into sqlite` - テーブルを SQLite データベースに変換

**関連システム・外部連携**：rusqlite クレートによる SQLite 連携。

**権限による制御**：特別な権限制御はなく、すべてのユーザーが実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLI専用機能（画面関連なし） |

## 機能種別

データクエリ / データベース操作

## 入力仕様

### 入力パラメータ

#### query db

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| SQL | String | Yes | 実行するSQL文 | - |
| --params, -p | Any | No | SQLパラメータ（リストまたはレコード） | - |

#### schema

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| - | - | - | パラメータなし | - |

#### into sqlite

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| file-name | String | Yes | データベースファイル名 | - |
| --table-name, -t | String | No | テーブル名（デフォルト: main） | - |

### 入力データソース

- **パイプライン入力（query db/schema）**: SQLiteDatabase（open コマンドまたは stor open で取得）
- **パイプライン入力（into sqlite）**: Table, Record, List

## 出力仕様

### 出力データ

| コマンド | 出力型 | 説明 |
|---------|--------|------|
| query db | List[Record] | クエリ結果のレコードリスト |
| schema | Record | スキーマ情報（tables, columns, constraints, foreign_keys, indexes） |
| into sqlite | Nothing | ファイル出力のため Nothing を返す |

### 出力先

- **query db, schema**: パイプラインへの出力（PipelineData）
- **into sqlite**: SQLite データベースファイル

## 処理フロー

### 処理シーケンス

```
1. パイプライン入力からSQLiteDatabaseを取得
   └─ SQLiteDatabase::try_from_pipeline()

2. SQL文の実行
   └─ query db: db.query() で SQL 実行
   └─ schema: get_tables(), get_columns() 等でスキーマ取得
   └─ into sqlite: トランザクション内で INSERT 実行

3. 結果の変換
   └─ SQLite の値を Nushell の値に変換
   └─ JSON(B) カラムは自動パース

4. 結果の返却
   └─ Value::list または Value::record として出力
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{コマンド種別}
    B -->|query db| C[SQLiteDatabase取得]
    B -->|schema| D[SQLiteDatabase取得]
    B -->|into sqlite| E[テーブル/レコード入力]
    C --> F[パラメータ解析]
    F --> G[db.query実行]
    G --> H[行をRecord変換]
    D --> I[get_tables]
    I --> J[各テーブルのスキーマ取得]
    J --> K[Record構築]
    E --> L[テーブル作成/確認]
    L --> M[トランザクション開始]
    M --> N[各行をINSERT]
    N --> O[コミット]
    H --> P[Value::list]
    K --> Q[Value::record]
    O --> R[Value::nothing]
    P --> S[PipelineData返却]
    Q --> S
    R --> S
    S --> T[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | パラメータ形式 | リストは位置パラメータ（?1, ?2）、レコードは名前付き（:name） | query db |
| BR-002 | JSON自動パース | JSON/JSONB 型のカラムは自動的に Nushell 値に変換 | query db |
| BR-003 | デフォルトテーブル名 | into sqlite のデフォルトテーブル名は "main" | into sqlite |
| BR-004 | NULL型推論 | 最初の行がNULLの場合、TEXT型として扱う（警告出力） | into sqlite |
| BR-005 | 型マッピング | List/Record/Table は JSONB として保存 | into sqlite |

### 計算ロジック

- **query db**: `nu_value_to_params()` でパラメータ変換、`prepared_statement_to_nu_list()` で結果取得
- **schema**: `get_tables()`, `get_columns()`, `get_constraints()`, `get_foreign_keys()`, `get_indexes()` を呼び出し
- **into sqlite**: `get_columns_with_sqlite_types()` で型推論、トランザクション内で INSERT 実行

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

### サポートするパラメータ形式

| 形式 | 例 | 説明 |
|------|-----|------|
| 位置パラメータ | `?1`, `?2` | リストで渡す |
| 名前付きパラメータ | `:name`, `@name`, `$name` | レコードで渡す |

### SQLite型からNushell型への変換

| SQLite型 | Nushell型 | 説明 |
|----------|----------|------|
| NULL | Nothing | null値 |
| INTEGER | Int | 整数 |
| REAL | Float | 浮動小数点 |
| TEXT | String | 文字列 |
| BLOB | Binary | バイナリ |
| JSON/JSONB | 元の型 | 自動パース |

### Nushell型からSQLite型への変換

| Nushell型 | SQLite型 | 説明 |
|----------|----------|------|
| String | TEXT | 文字列 |
| Int | INTEGER | 整数 |
| Float | REAL | 浮動小数点 |
| Binary | BLOB | バイナリ |
| Bool | BOOLEAN | 真偽値 |
| Date | DATETIME | 日時 |
| Duration | BIGINT | 期間 |
| Filesize | INTEGER | ファイルサイズ |
| List/Record/Table | JSONB | JSON形式 |
| Nothing | TEXT | NULL（警告付き） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| GenericError | SQLエラー | SQL構文エラー | SQL文を確認 |
| CantConvert | 変換エラー | SQLiteDatabase以外の入力 | open/stor openでDBを取得 |
| OnlySupportsThisInputType | 型エラー | サポートされない型 | サポートする型を使用 |
| TypeMismatch | パラメータエラー | 無効なパラメータ形式 | リストまたはレコードを使用 |

### リトライ仕様

リトライは不要。トランザクションはエラー時にロールバック。

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

- **into sqlite**: トランザクション内で全レコードを挿入、エラー時はロールバック
- **query db**: 自動コミットモード

## パフォーマンス要件

- query db の結果は全行をメモリに読み込む
- into sqlite はストリーム処理でメモリ効率良く挿入
- prepared statement でクエリを効率化

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

- パラメータ化クエリの使用を推奨（SQLインジェクション対策）
- 信頼できないソースからの SQL 実行に注意

## 備考

- JSON(B) カラムのサブフィールド取得時は `from json` での手動パースが必要
- schema コマンドは views, triggers には未対応

---

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

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

### 推奨読解順序

#### Step 1: モジュール構成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mod.rs | `crates/nu-command/src/database/commands/mod.rs` | コマンド一覧 |
| 1-2 | query.rs | `crates/nu-command/src/database/commands/query.rs` | 親コマンド（ヘルプ） |

**主要処理フロー**:
- **23行目（mod.rs）**: IntoSqliteDb, Query, QueryDb, SchemaDb の登録

#### Step 2: query db を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | query_db.rs | `crates/nu-command/src/database/commands/query_db.rs` | query db 実装 |

**主要処理フロー**:
- **12-28行目**: シグネチャ（SQL引数、--params）
- **105-108行目**: SQL文とパラメータの取得
- **110行目**: nu_value_to_params() でパラメータ変換
- **112-113行目**: db.query() で SQL 実行

#### Step 3: スキーマ取得を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | schema.rs | `crates/nu-command/src/database/commands/schema.rs` | schema 実装 |

**主要処理フロー**:
- **45-55行目**: get_tables() でテーブル一覧取得
- **58-76行目**: 各テーブルの columns, constraints, foreign_keys, indexes を取得
- **78行目**: tables レコードを構築

#### Step 4: SQLite変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | into_sqlite.rs | `crates/nu-command/src/database/commands/into_sqlite.rs` | into sqlite 実装 |

**主要処理フロー**:
- **11行目**: DEFAULT_TABLE_NAME = "main"
- **138-202行目**: try_init() でテーブル作成
- **248-330行目**: insert_in_transaction() でトランザクション挿入
- **370-404行目**: nu_value_to_sqlite_type() で型マッピング

#### Step 5: 共通処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | sqlite.rs | `crates/nu-command/src/database/values/sqlite.rs` | 共通関数 |

**主要処理フロー**:
- **437-465行目**: value_to_sql() で値変換
- **467-476行目**: values_to_sql() で複数値変換
- **489-530行目**: nu_value_to_params() でパラメータ変換
- **682-695行目**: convert_sqlite_row_to_nu_value() で行変換
- **697-718行目**: convert_sqlite_value_to_nu_value() で値変換

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

```
query (query.rs)
    │
    └─ query db (query_db.rs)
           └─ run()
                  └─ SQLiteDatabase::try_from_pipeline()
                  └─ nu_value_to_params()
                  └─ db.query()
                         └─ run_sql_query()
                                └─ prepared_statement_to_nu_list()
                                       └─ convert_sqlite_row_to_nu_value()

schema (schema.rs)
    └─ run()
           └─ SQLiteDatabase::try_from_pipeline()
           └─ db.get_tables()
           └─ get_table_columns()
           └─ get_table_constraints()
           └─ get_table_foreign_keys()
           └─ get_table_indexes()

into sqlite (into_sqlite.rs)
    └─ operate()
           └─ Table::new()
           └─ action()
                  └─ insert_in_transaction()
                         └─ table.try_init()
                         └─ values_to_sql()
                         └─ insert_statement.execute()
                         └─ tx.commit()
```

### データフロー図

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

SQLiteDatabase ───────▶ query db ─────────────────────────▶ List[Record]
        │                   │
        │                   └─ SQL実行 → 行変換
        │
SQLiteDatabase ───────▶ schema ───────────────────────────▶ Record
        │                   │
        │                   └─ スキーマ取得 → Record構築
        │
Table/Record ─────────▶ into sqlite ──────────────────────▶ Nothing
                            │
                            └─ CREATE TABLE → INSERT → ファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| mod.rs | `crates/nu-command/src/database/commands/mod.rs` | ソース | コマンド登録 |
| query.rs | `crates/nu-command/src/database/commands/query.rs` | ソース | 親コマンド |
| query_db.rs | `crates/nu-command/src/database/commands/query_db.rs` | ソース | query db 実装 |
| schema.rs | `crates/nu-command/src/database/commands/schema.rs` | ソース | schema 実装 |
| into_sqlite.rs | `crates/nu-command/src/database/commands/into_sqlite.rs` | ソース | into sqlite 実装 |
| sqlite.rs | `crates/nu-command/src/database/values/sqlite.rs` | ソース | 共通関数 |
