# 機能設計書：SQLクライアント

## 1. 機能概要

### 1.1 処理概要

SQLクライアント（flink-sql-client）は、Apache FlinkのSQL文を対話的に実行するためのコマンドラインインターフェースである。2つの動作モード（Embedded / Gateway）をサポートし、SQLの実行、結果表示、スクリプト実行を提供する。

主要なコンポーネント：
1. **SqlClient**: メインエントリーポイント
2. **CliClient**: CLI対話処理
3. **Executor**: SQL実行ゲートウェイ通信
4. **ResultView**: 結果表示（TABLE/CHANGELOG/TABLEAU）

### 1.2 業務上の目的・役割

| 項目 | 内容 |
|------|------|
| 目的 | FlinkのSQLを対話的・バッチ的に実行 |
| 役割 | SQL開発、デバッグ、クエリ実行の統一インターフェース |
| 主な利用場面 | アドホッククエリ、スクリプト実行、ジョブ投入 |

### 1.3 利用シーン・ユースケース

1. **対話モード**
   - SQLクエリの試行
   - テーブル・カタログ確認
   - 設定変更

2. **非対話モード**
   - SQLスクリプト実行
   - バッチジョブ投入
   - CI/CDパイプライン

3. **アプリケーションモード**
   - スクリプトをクラスターにデプロイ
   - 長時間実行ジョブ

## 2. 入出力仕様

### 2.1 起動モード

| モード | コマンド | 説明 |
|--------|---------|------|
| Embedded | `sql-client.sh embedded` | 組み込みゲートウェイモード |
| Gateway | `sql-client.sh gateway -e <address>` | 外部ゲートウェイ接続モード |
| デフォルト | `sql-client.sh` | Embeddedモードで起動 |

### 2.2 コマンドラインオプション

#### 2.2.1 Embeddedモード

| オプション | 説明 |
|-----------|------|
| `-f, --file <sqlFile>` | 実行するSQLファイル |
| `-i, --init <initFile>` | 初期化SQLファイル |
| `-h, --history <historyPath>` | 履歴ファイルパス |
| `-D <property=value>` | 設定プロパティ |
| `-j, --jar <jarPath>` | 追加JARファイル |
| `-l, --library <libraryPath>` | ライブラリディレクトリ |

#### 2.2.2 Gatewayモード

| オプション | 説明 |
|-----------|------|
| `-e, --endpoint <address>` | SQL Gatewayアドレス |
| `-s, --session-id <sessionId>` | セッションID |
| `-f, --file <sqlFile>` | 実行するSQLファイル |

### 2.3 結果表示モード（ResultMode）

| モード | 説明 |
|--------|------|
| TABLE | メモリにマテリアライズしてページ表示 |
| CHANGELOG | ストリーミング結果を変更ログとして表示 |
| TABLEAU | タブロー形式で画面に直接表示 |

### 2.4 Executorインターフェース

| メソッド | 入力 | 出力 | 説明 |
|---------|------|------|------|
| configureSession() | String statement | void | セッション設定 |
| getSessionConfig() | - | ReadableConfig | セッション設定取得 |
| executeStatement() | String statement | StatementResult | SQL実行 |
| completeStatement() | String, int | List<String> | 補完ヒント取得 |
| deployScript() | String/URI | String | スクリプトデプロイ |

## 3. 処理フロー

### 3.1 起動フロー

```mermaid
sequenceDiagram
    participant User as ユーザー
    participant Main as SqlClient.main()
    participant SC as SqlClient
    participant EG as EmbeddedGateway
    participant Exec as Executor
    participant CLI as CliClient

    User->>Main: sql-client.sh [mode] [options]
    Main->>Main: parseArgs(args)
    alt Gateway Mode
        Main->>SC: new SqlClient(isGatewayMode=true)
        SC->>Exec: Executor.create(address)
    else Embedded Mode
        Main->>SC: new SqlClient(isGatewayMode=false)
        SC->>EG: EmbeddedGateway.create()
        EG->>EG: SqlGateway.start()
        SC->>Exec: Executor.create(embeddedAddress)
    end
    SC->>CLI: new CliClient(executor)
    CLI->>CLI: executeInInteractiveMode() / executeInNonInteractiveMode()
```

### 3.2 対話モードフロー

```mermaid
sequenceDiagram
    participant User as ユーザー
    participant CLI as CliClient
    participant LR as LineReader
    participant Exec as Executor
    participant View as ResultView

    User->>CLI: executeInInteractiveMode()
    CLI->>LR: createLineReader()
    loop REPL
        CLI->>LR: readLine("Flink SQL> ")
        LR-->>CLI: SQL文
        CLI->>Exec: executeStatement(sql)
        Exec-->>CLI: StatementResult
        CLI->>View: 結果表示
        View-->>User: 出力
    end
```

### 3.3 結果表示フロー

```mermaid
flowchart TD
    subgraph Input["入力"]
        SR[StatementResult]
    end

    subgraph Mode["ResultMode判定"]
        SR --> RM{ResultMode}
        RM -->|TABLE| TV[CliTableResultView]
        RM -->|CHANGELOG| CV[CliChangelogResultView]
        RM -->|TABLEAU| TAV[CliTableauResultView]
    end

    subgraph Output["出力"]
        TV --> Display[画面表示]
        CV --> Display
        TAV --> Display
    end
```

## 4. 業務ルール・条件

### 4.1 モード選択ルール

| 条件 | 動作 |
|------|------|
| 引数なし | Embeddedモードで起動 |
| `embedded` | Embeddedモードで起動 |
| `gateway` | Gatewayモードで起動（-e必須） |
| `-f`指定 | 非対話モードで実行 |
| `-f`なし | 対話モードで実行 |

### 4.2 EmbeddedGateway

```java
// EmbeddedGateway.create()
NetUtils.Port port = NetUtils.getAvailablePort();
restConfig.set(SqlGatewayRestOptions.ADDRESS, "localhost");
restConfig.set(SqlGatewayRestOptions.PORT, port.getPort());
SqlGateway sqlGateway = new SqlGateway(config, new SingleSessionManager(defaultContext));
sqlGateway.start();
```

### 4.3 履歴ファイル

| OS | デフォルトパス |
|----|---------------|
| Windows | `%USERPROFILE%\flink-sql-history` |
| Unix/Mac | `~/.flink-sql-history` |

### 4.4 初期化ファイル

```java
// initFile実行
if (options.getInitFile() != null) {
    if (isApplicationMode(executor.getSessionConfig())) {
        throw new SqlClientException(
            "Sql Client doesn't support to run init files when deploying script into cluster.");
    }
    boolean success = cli.executeInitialization(options.getInitFile());
}
```

### 4.5 アプリケーションモード制約

| 機能 | サポート |
|------|---------|
| initFile実行 | 非対応 |
| スクリプトデプロイ | 対応 |

### 4.6 プロンプト表示

```java
// 通常プロンプト
"Flink SQL> "

// 行番号表示モード
"%N%M> "  // N=行番号, M=マルチライン継続
```

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

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

```
SqlClient (メインエントリーポイント)
├── main(String[] args)
│   └── startClient(args, terminalFactory)
│       ├── CliOptionsParser.parseEmbeddedModeClient() / parseGatewayModeClient()
│       └── new SqlClient(isGatewayMode, options, terminalFactory)
│
├── start()
│   ├── Gateway Mode
│   │   └── Executor.create(address, sessionId)
│   │
│   └── Embedded Mode
│       ├── EmbeddedGateway.create(defaultContext)
│       │   └── SqlGateway.start()
│       └── Executor.create(embeddedAddress, sessionId)
│
└── openCli(Executor executor)
    └── new CliClient(terminalFactory, executor, historyFilePath)
        ├── executeInitialization(initFile)
        ├── executeInInteractiveMode()
        │   ├── createLineReader()
        │   └── executeInteractive()
        │       ├── readLine()
        │       ├── executor.executeStatement()
        │       └── 結果表示
        │
        └── executeInNonInteractiveMode(sqlFile)
            ├── isApplicationMode?
            │   └── executor.deployScript()
            └── 通常実行

Executor (SQLゲートウェイ通信)
├── configureSession(String statement)
├── getSessionConfig() → ReadableConfig
├── getSessionConfigMap() → Map<String, String>
├── executeStatement(String statement) → StatementResult
├── completeStatement(String, int) → List<String>
└── deployScript(String/URI) → String clusterId

CliClient (CLI対話処理)
├── executeInInteractiveMode()
├── executeInNonInteractiveMode(URI)
├── executeInitialization(String initFile)
└── close()

ResultMode (結果表示モード)
├── TABLE   → CliTableResultView
├── CHANGELOG → CliChangelogResultView
└── TABLEAU → CliTableauResultView
```

### 5.2 データフロー図

```
[ユーザー入力]
     │
     ▼
[sql-client.sh args]
     │
     ▼
[SqlClient.main()]
     │
     ├── モード判定
     │   ├── embedded → EmbeddedGateway起動
     │   └── gateway → 外部接続
     │
     ▼
[Executor生成]
     │
     ▼
[CliClient]
     │
     ├── 対話モード
     │   └── LineReader → SQL入力 → executeStatement → 結果表示
     │
     └── 非対話モード
         └── SQLファイル読み込み → executeStatement → 結果出力
               │
               ▼
         [アプリケーションモード時]
               └── deployScript → クラスターID
```

### 5.3 関連ファイル一覧

| ファイルパス | 役割 |
|-------------|------|
| `flink-sql-client/.../SqlClient.java` | メインエントリーポイント |
| `flink-sql-client/.../cli/CliClient.java` | CLI対話処理 |
| `flink-sql-client/.../cli/CliOptions.java` | CLIオプション定義 |
| `flink-sql-client/.../cli/CliOptionsParser.java` | CLIオプション解析 |
| `flink-sql-client/.../gateway/Executor.java` | SQL実行インターフェース |
| `flink-sql-client/.../gateway/ExecutorImpl.java` | SQL実行実装 |
| `flink-sql-client/.../config/ResultMode.java` | 結果表示モード |
| `flink-sql-client/.../config/SqlClientOptions.java` | クライアント設定 |
| `flink-sql-client/.../cli/CliTableResultView.java` | TABLE結果表示 |
| `flink-sql-client/.../cli/CliChangelogResultView.java` | CHANGELOG結果表示 |
| `flink-sql-client/.../cli/CliTableauResultView.java` | TABLEAU結果表示 |
| `flink-sql-client/.../cli/parser/SqlMultiLineParser.java` | 複数行SQL解析 |
| `flink-sql-client/.../cli/SqlCompleter.java` | SQL補完 |

### 5.4 コードリーディング手順

#### Step 1: SqlClientのエントリーポイント

```java
// SqlClient.java (53-63行目)
/**
 * SQL Client for submitting SQL statements. The client can be executed in two modes:
 * a gateway and embedded mode.
 *
 * - In embedded mode, the SQL CLI is tightly coupled with the executor in a common process.
 * - In gateway mode, the SQL CLI client connects to the REST API of the gateway.
 */
public class SqlClient {
    public static final String MODE_EMBEDDED = "embedded";
    public static final String MODE_GATEWAY = "gateway";
    public static final String MODE_NONE = "";
}
```

#### Step 2: 起動処理

```java
// SqlClient.java (82-117行目)
private void start() {
    if (isGatewayMode) {
        // Gateway Mode
        try (Executor executor = Executor.create(
                DefaultContextUtils.buildDefaultContext(gatewayCliOptions),
                gatewayCliOptions.getGatewayAddress().orElseThrow(...),
                options.getSessionId())) {
            Runtime.getRuntime().addShutdownHook(new ShutdownThread(executor));
            openCli(executor);
        }
    } else {
        // Embedded Mode
        try (EmbeddedGateway embeddedGateway = EmbeddedGateway.create(defaultContext);
             Executor executor = Executor.create(
                 defaultContext,
                 InetSocketAddress.createUnresolved(
                     embeddedGateway.getAddress(),
                     embeddedGateway.getPort()),
                 options.getSessionId())) {
            Runtime.getRuntime().addShutdownHook(new ShutdownThread(executor, embeddedGateway));
            openCli(executor);
        }
    }
}
```

#### Step 3: CliClientの対話処理

```java
// CliClient.java (66-78行目)
/** SQL CLI client. */
public class CliClient implements AutoCloseable {
    private static final String NEWLINE_PROMPT =
        new AttributedStringBuilder()
            .style(AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN))
            .append("Flink SQL")
            .style(AttributedStyle.DEFAULT)
            .append("> ")
            .toAnsi();
}
```

#### Step 4: Executorインターフェース

```java
// Executor.java (34-104行目)
/** A gateway for communicating with Flink and other external systems. */
public interface Executor extends Closeable {
    static Executor create(DefaultContext, InetSocketAddress, String sessionId);

    void configureSession(String statement);
    ReadableConfig getSessionConfig();
    Map<String, String> getSessionConfigMap();
    StatementResult executeStatement(String statement);
    List<String> completeStatement(String statement, int position);
    String deployScript(@Nullable String script, @Nullable URI uri);
    void close();
}
```

#### Step 5: ResultMode

```java
// ResultMode.java (27-48行目)
/** The mode when display the result of the query in the sql client. */
@PublicEvolving
public enum ResultMode implements DescribedEnum {
    TABLE(text("Materializes results in memory and visualizes them in a regular, paginated table representation.")),
    CHANGELOG(text("Visualizes the result stream that is produced by a continuous query.")),
    TABLEAU(text("Display results in the screen directly in a tableau format."));
}
```

## 6. 関連機能・API

### 6.1 関連機能

| 機能名 | 関連種別 | 説明 |
|--------|---------|------|
| SQLパーサー（No.18） | 連携機能 | SQL文の解析 |
| SQLプランナー（No.19） | 連携機能 | 実行計画生成 |
| Table API Java（No.13） | 内部利用 | TableEnvironment経由の実行 |

### 6.2 使用例

```bash
# Embeddedモードで対話起動
./bin/sql-client.sh

# Embeddedモードでスクリプト実行
./bin/sql-client.sh embedded -f script.sql

# 初期化ファイル付き起動
./bin/sql-client.sh embedded -i init.sql

# Gatewayモードで接続
./bin/sql-client.sh gateway -e localhost:8083

# 設定指定
./bin/sql-client.sh embedded -D execution.runtime-mode=streaming

# JAR追加
./bin/sql-client.sh embedded -j /path/to/connector.jar
```

## 7. 設計上の考慮事項

### 7.1 拡張性

| 観点 | 説明 |
|------|------|
| モード拡張 | 新しい接続モードを追加可能 |
| ResultMode拡張 | 新しい結果表示モードを追加可能 |
| コマンド拡張 | カスタムコマンドの追加が可能 |

### 7.2 ユーザビリティ

| 機能 | 説明 |
|------|------|
| シンタックスハイライト | SqlClientSyntaxHighlighterで実現 |
| 自動補完 | SqlCompleterで実現 |
| 履歴機能 | JLineのHistory機能で実現 |
| マルチライン入力 | SqlMultiLineParserで実現 |

### 7.3 制約事項

| 制約 | 説明 |
|------|------|
| アプリケーションモード | initFileは非対応 |
| Gatewayモード | -eオプション必須 |
| 結果サイズ | TABLEモードはメモリ制限あり |

## 8. 用語集

| 用語 | 説明 |
|------|------|
| SqlClient | SQL CLIのメインクラス |
| CliClient | CLI対話処理クラス |
| Executor | SQL実行ゲートウェイ通信インターフェース |
| EmbeddedGateway | 組み込みSQL Gateway |
| ResultMode | 結果表示モード（TABLE/CHANGELOG/TABLEAU） |
| SingleSessionManager | 単一セッション管理 |
| SqlGateway | SQL Gateway本体 |
| LineReader | JLineの行読み取り |
| SqlCompleter | SQL自動補完 |
| SqlMultiLineParser | 複数行SQL解析 |
