# 機能設計書 74-データクエリ実行

## 概要

本ドキュメントは、Giselleワークフローシステムにおけるデータクエリ実行機能の設計を記述する。この機能は、ワークフロー内のデータクエリノードから外部PostgreSQLデータベースに対してSQLクエリを実行し、結果を取得する。

### 本機能の処理概要

**業務上の目的・背景**：ワークフローにおいて、外部データベースから情報を取得し、その結果をAIテキスト生成やアクション実行に活用する必要がある。この機能は、データクエリノードに設定されたSQLクエリを安全に実行し、結果セットをワークフローの後続ノードで利用可能な形式で返却する。プレースホルダー置換とパラメタライズドクエリにより、SQLインジェクション攻撃からの保護も提供する。

**機能の利用シーン**：
- 顧客データベースから特定条件に合致するレコードを抽出し、AIでレポートを生成する場面
- トリガーイベントの情報に基づいて関連データを検索する場面
- LLMが生成したSQLクエリを実行して動的なデータ取得を行う場面

**主要な処理内容**：
1. ジェネレーションコンテキストからデータクエリノードと接続情報を抽出
2. クエリ内のプレースホルダー（{{nodeId:outputId}}形式）を解決
3. ユーザー入力値をパラメタライズドクエリに変換してSQLインジェクションを防止
4. データストア設定を取得し、暗号化された接続文字列を復号
5. PostgreSQL接続プールを作成し、クエリを実行
6. キャンセル検出のためのポーリング機構を提供
7. 結果をジェネレーション出力として保存

**関連システム・外部連携**：
- GiselleStorageを介したデータストア設定・シークレットの取得
- Vaultを介した接続文字列の復号
- pgライブラリを使用したPostgreSQLデータベースへの接続
- ジェネレーションエグゼキューターフレームワークとの統合

**権限による制御**：データストアノードに設定されたシークレット（接続文字列）へのアクセス権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | ワークフローエディタ画面 | 補助機能 | データクエリノードの追加・編集 |

## 機能種別

データ取得処理・外部システム連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| context | GiselleContext | Yes | Giselleの実行コンテキスト | - |
| generation | QueuedGeneration | Yes | 実行対象のキュー済みジェネレーション | データクエリノードに関連付けられていること |
| metadata | GenerationMetadata | No | ジェネレーションのメタデータ | - |

### 入力データソース

- ジェネレーションコンテキスト内のデータクエリノード設定
- 接続されたソースノード（テキストノード、テキスト生成ノード等）からの入力値
- データストア設定ファイル
- 暗号化されたシークレット（接続文字列）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| GenerationOutput | data-query-result | ジェネレーション出力オブジェクト |
| outputId | OutputId | 出力スロットのID |
| content.type | "data-query" | コンテンツ種別 |
| content.dataStoreId | DataStoreId | 実行対象のデータストアID |
| content.rows | unknown[] | クエリ結果の行データ |
| content.rowCount | number | 結果行数 |
| content.query | string | 実行されたクエリ（表示用、実値埋め込み済み） |

### 出力先

- ジェネレーションストレージに完了状態として保存
- 後続ノードで利用可能な出力として公開

## 処理フロー

### 処理シーケンス

```
1. ジェネレーション実行開始
   └─ useGenerationExecutorによるラッピング

2. ノード種別確認
   └─ isDataQueryNode()でデータクエリノードであることを確認

3. クエリの解決
   └─ resolveQuery()でプレースホルダーを置換
       ├─ テキストノード → パラメタライズドクエリ化（SQLインジェクション防止）
       ├─ テキスト生成ノード → 直接置換（LLM生成SQLを実行）
       └─ その他ノード → ノードタイプに応じた処理

4. データストアノードの取得
   └─ 接続されたDataStoreノードを検索・検証

5. データストア設定の取得
   └─ getDataStore()でストレージから設定を取得

6. 接続文字列の取得
   └─ シークレットを読み込み、Vaultで復号

7. キャンセル確認
   └─ 実行前にキャンセル状態をチェック

8. 接続プール作成
   └─ タイムアウト設定付きでPoolを初期化

9. ポーリングクエリ実行
   └─ createPollingQueryExecutor()でキャンセル検知付き実行

10. 結果の処理
    └─ QueryResultから行データと行数を抽出

11. ジェネレーション完了
    └─ finishGeneration()で結果を保存
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{データクエリノード?}
    B -->|No| C[Error: Invalid generation type]
    B -->|Yes| D[resolveQuery: プレースホルダー解決]
    D --> E[データストアノード検索]
    E --> F{ノード存在?}
    F -->|No| G[Error: No Data Store node]
    F -->|Yes| H{configured状態?}
    H -->|No| I[Error: Not configured]
    H -->|Yes| J[getDataStore: 設定取得]
    J --> K{設定存在?}
    K -->|No| L[Error: DataStore not found]
    K -->|Yes| M[シークレット取得・復号]
    M --> N{キャンセル確認}
    N -->|キャンセル済み| O[処理終了]
    N -->|継続| P[接続プール作成]
    P --> Q[createPollingQueryExecutor]
    Q --> R[クエリ実行]
    R --> S{実行成功?}
    S -->|エラー| T{キャンセルによる?}
    T -->|Yes| O
    T -->|No| U[DataQueryError throw]
    S -->|成功| V[結果データ抽出]
    V --> W{キャンセル確認}
    W -->|キャンセル済み| O
    W -->|継続| X[finishGeneration]
    X --> Y[終了]
    O --> Y
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | パラメタライズ | テキストノードからの入力はパラメタライズドクエリとして安全に処理 | テキストノード接続時 |
| BR-02 | LLM生成SQL直接実行 | テキスト生成ノードからの出力は直接置換（LLM生成クエリを許可） | テキスト生成ノード接続時 |
| BR-03 | コードブロック除去 | LLM出力からMarkdownコードブロック（```sql...```）を除去 | テキスト生成ノード接続時 |
| BR-04 | 複数ステートメント対応 | 複数SQLステートメント実行時は最後の結果を返却 | 常時 |
| BR-05 | キャンセル検知 | 2.5秒間隔でキャンセル状態をポーリング | クエリ実行中 |
| BR-06 | タイムアウト設定 | 接続10秒、クエリ65秒、ステートメント60秒 | 常時 |

### 計算ロジック

パラメタライズドクエリ変換：
```typescript
// '{{nodeId:outputId}}' → $1 （引用符も除去）
// {{nodeId:outputId}} → $1
// 同一プレースホルダーは同一パラメータインデックスを再利用
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| クエリ実行 | 外部PostgreSQLテーブル | SELECT（主に） | ユーザー定義クエリを実行 |

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

外部データベースへの操作は、ユーザーが定義したSQLクエリに依存する。本機能はSELECT以外のクエリも実行可能であり、UPDATE/DELETE/INSERT等も許可される。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Invalid generation type | データクエリノード以外で呼び出し | 正しいノードタイプを使用 |
| - | No Data Store node | データストアノード未接続 | ノードを接続 |
| - | Not configured | データストアノード未設定 | データストアを設定 |
| - | DataStore not found | データストア設定ファイルなし | データストアを再作成 |
| - | DataQueryError | クエリ実行エラー | SQLの文法・権限を確認 |
| 57014 | query_canceled | タイムアウトまたはキャンセル | タイムアウト設定見直しまたは意図的キャンセル |

### リトライ仕様

本関数自体にはリトライロジックは含まれない。クエリ実行エラーは即座にDataQueryErrorとしてスローされる。

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

- 外部データベースへのクエリは自動コミットモードで実行
- 複数ステートメント実行時も各ステートメントは独立して実行
- 本機能でトランザクション制御（BEGIN/COMMIT/ROLLBACK）を行う場合は、クエリ内に明示的に記述

## パフォーマンス要件

- 接続タイムアウト: 10秒
- クエリタイムアウト: 65秒
- ステートメントタイムアウト: 60秒
- キャンセルポーリング間隔: 2.5秒

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

- テキストノードからの入力はパラメタライズドクエリにより、SQLインジェクション攻撃から保護
- テキスト生成ノード（LLM出力）は直接実行されるため、プロンプトインジェクションによる意図しないSQLが実行される可能性がある（設計上の意図）
- 接続文字列はVaultで暗号化されて保存、実行時に復号
- データベース権限は接続ユーザーの権限に依存（最小権限原則の適用を推奨）

## 備考

- ファイル、画像、ベクトルストア等のノードタイプはデータクエリのソースとして未サポート
- PostgreSQL固有のエラーコード（57014等）を考慮した処理が実装されている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/protocol/src/data-store/index.ts` | DataStore型定義 |
| 1-2 | data-query.ts | `packages/protocol/src/node/operations/data-query.ts` | DataQueryNode型定義 |

**読解のコツ**: GenerationOutputの`data-query-result`タイプとその構造を理解することが重要。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | execute-data-query.ts | `packages/giselle/src/operations/execute-data-query.ts` | メイン関数の全体構造 |

**主要処理フロー**:
1. **126-130行目**: 関数シグネチャとuseGenerationExecutorの使用
2. **143-146行目**: isDataQueryNodeでノード種別確認
3. **148-153行目**: resolveQueryでクエリ解決
4. **155-167行目**: データストアノードの検索と検証
5. **169-180行目**: データストア設定の取得と設定パース
6. **182-188行目**: シークレットの取得と復号
7. **198-203行目**: Poolの作成（タイムアウト設定）
8. **205-211行目**: ポーリングクエリエグゼキューターの作成
9. **214-239行目**: クエリ実行とエラーハンドリング
10. **258-275行目**: ジェネレーション出力の構築と完了

#### Step 3: クエリ解決ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | execute-data-query.ts | `packages/giselle/src/operations/execute-data-query.ts` | resolveQuery関数（381-589行目） |

**主要処理フロー**:
- **345-379行目**: parameterizeQuery - パラメタライズドクエリの構築
- **486-487行目**: baseQueryの取得（JSON Content対応）
- **491-495行目**: プレースホルダーパターンのマッチング
- **502-578行目**: ノードタイプに応じた置換処理

#### Step 4: ポーリングエグゼキューターを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | execute-data-query.ts | `packages/giselle/src/operations/execute-data-query.ts` | createPollingQueryExecutor関数（51-124行目） |

**主要処理フロー**:
- **70-73行目**: クライアント接続とPID取得
- **79-94行目**: ポーリングループ（2.5秒間隔）
- **84行目**: pg_cancel_backendによるクエリキャンセル
- **97-103行目**: クエリ実行と複数ステートメント対応
- **108-121行目**: クリーンアップ処理

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

```
executeDataQuery()
    │
    ├─ useGenerationExecutor()
    │      └─ ジェネレーション実行フレームワーク
    │
    ├─ resolveQuery()
    │      ├─ jsonContentToPlainText() - JSON Content変換
    │      ├─ findGeneration() - 前段ノードの生成結果取得
    │      ├─ generationContentResolver() - 出力内容の解決
    │      ├─ stripCodeBlock() - Markdownコードブロック除去
    │      └─ parameterizeQuery() - パラメタライズ処理
    │
    ├─ getDataStore()
    │      └─ データストア設定取得
    │
    ├─ parseConfiguration()
    │      └─ プロバイダ設定パース
    │
    ├─ context.vault.decrypt()
    │      └─ 接続文字列復号
    │
    ├─ new Pool()
    │      └─ PostgreSQL接続プール作成
    │
    ├─ createPollingQueryExecutor()
    │      ├─ execute()
    │      │      ├─ pool.connect()
    │      │      ├─ pg_backend_pid() - PID取得
    │      │      ├─ poll() - キャンセルポーリング
    │      │      └─ client.query() - クエリ実行
    │      └─ cleanup()
    │             └─ pool.end()
    │
    └─ finishGeneration()
           └─ ジェネレーション完了処理
```

### データフロー図

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

QueuedGeneration ────────▶ useGenerationExecutor ────────▶ RunningGeneration
      │                                                           │
      ▼                                                           ▼
GenerationContext ────────▶ resolveQuery() ────────────▶ パラメタライズドクエリ
      │                           │                               │
      │                           ├─ テキストノード値              │
      │                           ├─ テキスト生成結果              │
      │                           └─ トリガー出力                  │
      │                                                           │
      ▼                                                           ▼
DataStoreNode ────────────▶ getDataStore() ────────────▶ 接続設定
                                  │
                                  ▼
                           vault.decrypt() ────────────▶ 接続文字列
                                  │
                                  ▼
                           Pool + Executor ────────────▶ QueryResult
                                  │
                                  ▼
                           finishGeneration() ─────────▶ CompletedGeneration
                                                               │
                                                               ▼
                                                    GenerationOutput
                                                    (data-query-result)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| execute-data-query.ts | `packages/giselle/src/operations/execute-data-query.ts` | ソース | メイン関数 |
| get-data-store.ts | `packages/giselle/src/data-stores/get-data-store.ts` | ソース | データストア設定取得 |
| paths.ts | `packages/giselle/src/secrets/paths.ts` | ソース | シークレットパス生成 |
| index.ts | `packages/protocol/src/data-store/index.ts` | ソース | DataStore型定義 |
| use-generation-executor.ts | `packages/giselle/src/generations/internal/use-generation-executor.ts` | ソース | ジェネレーション実行フレームワーク |
| error.ts | `packages/giselle/src/error.ts` | ソース | DataQueryErrorクラス |
