# 機能設計書：SQLパーサー

## 1. 機能概要

### 1.1 処理概要

SQLパーサー（flink-sql-parser）は、Apache Flink Table/SQLのSQL文を解析し、抽象構文木（AST）に変換する機能である。Apache Calciteをベースとし、Flink固有のSQL拡張をサポートする。

主要なコンポーネント：
1. **FlinkSqlParserImpl**: Flink SQL用のパーサー実装（JavaCC生成）
2. **CalciteParser**: SqlParserのラッパー
3. **SqlNode拡張**: Flink固有のDDL/DML/DQLノード
4. **FlinkSqlConformance**: FlinkのSQL準拠設定

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

| 項目 | 内容 |
|------|------|
| 目的 | SQL文字列を構造化された抽象構文木に変換 |
| 役割 | SQLクエリの構文解析・検証の第一段階を担当 |
| 主な利用場面 | TableEnvironment.sqlQuery()、SQL CLI等からのSQL実行 |

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

1. **DDL解析**
   - CREATE TABLE/VIEW/DATABASE/CATALOG
   - ALTER/DROP文
   - SHOW/DESCRIBE文

2. **DML解析**
   - INSERT INTO/OVERWRITE
   - SELECT文（クエリ）
   - STATEMENT SET

3. **拡張構文**
   - ウォーターマーク定義
   - 計算カラム
   - パーティショニング
   - テーブルヒント

## 2. 入出力仕様

### 2.1 入力仕様

| 項目 | 型 | 説明 |
|------|-----|------|
| SQL文字列 | String | 解析対象のSQL文 |
| SqlParser.Config | Config | パーサー設定 |

### 2.2 出力仕様

| メソッド | 出力型 | 説明 |
|---------|-------|------|
| parse(String) | SqlNode | 単一SQL文のAST |
| parseSqlList(String) | SqlNodeList | 複数SQL文のAST |
| parseExpression(String) | SqlNode | SQL式のAST |
| parseIdentifier(String) | SqlIdentifier | 識別子のAST |

### 2.3 主要なSqlNode種別

#### 2.3.1 DDL（Data Definition Language）

| クラス | SQL | 説明 |
|--------|-----|------|
| SqlCreateTable | CREATE TABLE | テーブル作成 |
| SqlCreateView | CREATE VIEW | ビュー作成 |
| SqlCreateDatabase | CREATE DATABASE | データベース作成 |
| SqlCreateCatalog | CREATE CATALOG | カタログ作成 |
| SqlCreateFunction | CREATE FUNCTION | 関数作成 |
| SqlDropTable | DROP TABLE | テーブル削除 |
| SqlAlterTable | ALTER TABLE | テーブル変更 |

#### 2.3.2 DML（Data Manipulation Language）

| クラス | SQL | 説明 |
|--------|-----|------|
| RichSqlInsert | INSERT INTO/OVERWRITE | 拡張INSERT文 |
| SqlStatementSet | BEGIN/END STATEMENT SET | 複数INSERT |
| SqlTruncateTable | TRUNCATE TABLE | テーブル切り詰め |

#### 2.3.3 DQL（Data Query Language）

| クラス | SQL | 説明 |
|--------|-----|------|
| SqlShowTables | SHOW TABLES | テーブル一覧 |
| SqlShowDatabases | SHOW DATABASES | データベース一覧 |
| SqlShowCatalogs | SHOW CATALOGS | カタログ一覧 |
| SqlShowFunctions | SHOW FUNCTIONS | 関数一覧 |
| SqlRichDescribeTable | DESCRIBE TABLE | テーブル詳細 |
| SqlRichExplain | EXPLAIN | 実行計画 |

## 3. 処理フロー

### 3.1 パース処理フロー

```mermaid
sequenceDiagram
    participant User as ユーザー
    participant TE as TableEnvironment
    participant CP as CalciteParser
    participant SP as SqlParser
    participant FP as FlinkSqlParserImpl
    participant SN as SqlNode

    User->>TE: sqlQuery(sql)
    TE->>CP: parse(sql)
    CP->>SP: SqlParser.create(sql, config)
    SP->>FP: parseStmt()
    FP->>FP: 字句解析（トークン化）
    FP->>FP: 構文解析（AST構築）
    FP-->>SP: SqlNode
    SP-->>CP: SqlNode
    CP-->>TE: SqlNode
    Note over TE: 次段階：Planner
```

### 3.2 SqlNode階層構造

```mermaid
classDiagram
    class SqlNode {
        <<abstract>>
        +unparse(SqlWriter, int, int)
        +getKind() SqlKind
        +getOperator() SqlOperator
    }

    class SqlCall {
        <<abstract>>
        +getOperandList() List~SqlNode~
    }

    class SqlDdl {
        <<abstract>>
    }

    class SqlCreateObject {
        +name SqlIdentifier
        +isTemporary boolean
        +ifNotExists boolean
    }

    class SqlCreateTable {
        +columnList SqlNodeList
        +tableConstraints List
        +partitionKeyList SqlNodeList
        +watermark SqlWatermark
    }

    class SqlInsert {
        +targetTable SqlNode
        +source SqlNode
        +columnList SqlNodeList
    }

    class RichSqlInsert {
        +staticPartitions SqlNodeList
        +extendedKeywords SqlNodeList
        +isOverwrite() boolean
    }

    SqlNode <|-- SqlCall
    SqlCall <|-- SqlDdl
    SqlCall <|-- SqlInsert
    SqlDdl <|-- SqlCreateObject
    SqlCreateObject <|-- SqlCreateTable
    SqlInsert <|-- RichSqlInsert
```

### 3.3 パーサー設定フロー

```mermaid
flowchart TD
    subgraph Config["SqlParser.Config"]
        P1[parserFactory] --> FP[FlinkSqlParserImpl.FACTORY]
        P2[conformance] --> FC[FlinkSqlConformance]
        P3[quoting] --> Q[BACK_TICK/DOUBLE_QUOTE]
        P4[identifierMaxLength] --> L[128]
    end

    subgraph Parser["パーサー生成"]
        Config --> SP[SqlParser.create]
        SP --> FPI[FlinkSqlParserImpl]
    end

    subgraph Parse["解析処理"]
        FPI --> PS[parseStmt]
        FPI --> PSL[parseStmtList]
        FPI --> PE[parseExpression]
    end
```

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

### 4.1 FlinkSqlConformance設定

| 設定 | 値 | 説明 |
|------|-----|------|
| isSortByOrdinal | true | ORDER BY 数値指定を許可 |
| isSortByAlias | true | ORDER BY エイリアス指定を許可 |
| isPercentRemainderAllowed | true | %演算子を許可 |
| allowNiladicParentheses | true | 引数なし関数の()を許可 |
| allowExplicitRowValueConstructor | true | ROW(...)を許可 |
| isValueAllowed | true | VALUES句を許可 |

### 4.2 CREATE TABLE構文

```sql
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] table_name
(
  column_name data_type [constraint] [COMMENT column_comment],
  ...
  [PRIMARY KEY (column_name, ...)]
  [watermark_definition]
)
[COMMENT table_comment]
[PARTITIONED BY (column_name, ...)]
[DISTRIBUTED BY (column_name, ...)]
WITH (
  'property_name' = 'property_value',
  ...
)
```

### 4.3 RichSqlInsert拡張

| 拡張 | 構文 | 説明 |
|------|------|------|
| OVERWRITE | INSERT OVERWRITE | 上書きモード |
| パーティション | PARTITION(...) | 静的パーティション指定 |
| テーブルヒント | /*+ OPTIONS(...) */ | テーブルオプション |
| ON CONFLICT | ON CONFLICT DO ... | 競合時動作 |

```sql
-- 拡張INSERT例
INSERT OVERWRITE table_name
PARTITION (part_col = 'value')
SELECT * FROM source_table
```

### 4.4 SqlShowTables種別

| SqlTableKind | SQL | 説明 |
|--------------|-----|------|
| TABLE | SHOW TABLES | 通常テーブル |
| VIEW | SHOW VIEWS | ビュー |
| MATERIALIZED_TABLE | SHOW MATERIALIZED TABLES | マテリアライズドテーブル |

### 4.5 エラー処理

| 例外 | 条件 | 説明 |
|------|------|------|
| SqlParserException | 構文エラー | SQL文の構文が不正 |
| SqlParserEOFException | EOF到達 | SQL文が不完全 |
| SqlValidateException | 検証エラー | 制約違反等 |

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

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

```
CalciteParser
├── parse(String sql) → SqlNode
│   ├── SqlParser.create(sql, config)
│   ├── parser.parseStmt()
│   └── 例外変換: SqlParseException → SqlParserException/SqlParserEOFException
│
├── parseSqlList(String sql) → SqlNodeList
│   └── parser.parseStmtList()
│
├── parseExpression(String sqlExpression) → SqlNode
│   └── parser.parseExpression()
│
└── parseIdentifier(String identifier) → SqlIdentifier
    └── FlinkSqlParserImpl.TableApiIdentifier()

FlinkSqlParserImpl (JavaCC生成)
├── DDL解析
│   ├── SqlCreateTable → columnList, constraints, watermark, partition
│   ├── SqlCreateView → asQuery
│   ├── SqlCreateDatabase → properties
│   └── SqlCreateCatalog → properties
│
├── DML解析
│   ├── RichSqlInsert → staticPartitions, extendedKeywords, conflictAction
│   ├── SqlStatementSet → insertStatements
│   └── SqlTruncateTable
│
└── DQL解析
    ├── SqlShowTables → kind, preposition, databaseName, likeLiteral
    ├── SqlShowDatabases
    ├── SqlShowCatalogs
    └── SqlRichExplain
```

### 5.2 データフロー図

```
[SQL文字列]
     │
     ▼
[CalciteParser.parse()]
     │
     ├── SqlParser.Config
     │   ├── parserFactory: FlinkSqlParserImpl.FACTORY
     │   ├── conformance: FlinkSqlConformance.DEFAULT
     │   ├── quoting: BACK_TICK
     │   └── caseSensitive: true
     │
     ▼
[FlinkSqlParserImpl]
     │
     ├── 字句解析 (Lexer)
     │   └── トークン生成
     │
     ├── 構文解析 (Parser)
     │   └── AST構築
     │
     ▼
[SqlNode]
     │
     ├── DDL: SqlCreateTable, SqlCreateView, ...
     ├── DML: RichSqlInsert, SqlStatementSet, ...
     └── DQL: SqlShowTables, SqlRichExplain, ...
           │
           ▼
     [Planner処理へ]
```

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

| ファイルパス | 役割 |
|-------------|------|
| `flink-table-planner/.../CalciteParser.java` | SqlParserラッパー |
| `flink-sql-parser/.../FlinkSqlParserImpl.java` | 生成パーサー（JavaCC） |
| `flink-sql-parser/.../validate/FlinkSqlConformance.java` | SQL準拠設定 |
| `flink-sql-parser/.../ddl/table/SqlCreateTable.java` | CREATE TABLE DDL |
| `flink-sql-parser/.../ddl/SqlCreateView.java` | CREATE VIEW DDL |
| `flink-sql-parser/.../ddl/SqlCreateDatabase.java` | CREATE DATABASE DDL |
| `flink-sql-parser/.../ddl/catalog/SqlCreateCatalog.java` | CREATE CATALOG DDL |
| `flink-sql-parser/.../dml/RichSqlInsert.java` | 拡張INSERT DML |
| `flink-sql-parser/.../dml/SqlStatementSet.java` | STATEMENT SET |
| `flink-sql-parser/.../dql/SqlShowTables.java` | SHOW TABLES DQL |
| `flink-sql-parser/.../dql/SqlShowDatabases.java` | SHOW DATABASES DQL |
| `flink-sql-parser/.../dql/SqlRichExplain.java` | EXPLAIN DQL |
| `flink-sql-parser/src/main/codegen/templates/Parser.jj` | パーサー文法定義 |

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

#### Step 1: CalciteParserの理解

```java
// CalciteParser.java (35-63行目)
/**
 * Thin wrapper around SqlParser that does exception conversion and SqlNode casting.
 */
public class CalciteParser {
    private final SqlParser.Config config;

    public SqlNode parse(String sql) {
        try {
            SqlParser parser = SqlParser.create(sql, config);
            return parser.parseStmt();
        } catch (SqlParseException e) {
            if (e.getMessage().contains("Encountered \"<EOF>\"")) {
                throw new SqlParserEOFException(e.getMessage(), e);
            }
            throw new SqlParserException("SQL parse failed. " + e.getMessage(), e);
        }
    }
}
```

#### Step 2: FlinkSqlConformanceの設定

```java
// FlinkSqlConformance.java (25-28行目)
/** Sql conformance used for flink to set specific sql dialect parser. */
public enum FlinkSqlConformance implements SqlConformance {
    /** Calcite's default SQL behavior. */
    DEFAULT;

    @Override
    public boolean isSortByOrdinal() {
        return true;  // ORDER BY 1, 2を許可
    }

    @Override
    public boolean isSortByAlias() {
        return true;  // ORDER BY aliasを許可
    }
}
```

#### Step 3: SqlCreateTableの構造

```java
// SqlCreateTable.java (53-120行目)
/** CREATE TABLE DDL sql call. */
public class SqlCreateTable extends SqlCreateObject implements ExtendedSqlNode {
    private final SqlNodeList columnList;
    private final List<SqlTableConstraint> tableConstraints;
    private final SqlDistribution distribution;
    protected final SqlNodeList partitionKeyList;
    private final SqlWatermark watermark;

    @Override
    public void validate() throws SqlValidateException {
        SqlConstraintValidator.validateAndChangeColumnNullability(tableConstraints, columnList);
    }
}
```

#### Step 4: RichSqlInsertの拡張機能

```java
// RichSqlInsert.java (39-94行目)
/** A SqlInsert that have some extension functions like partition, overwrite. */
public class RichSqlInsert extends SqlInsert {
    private final SqlNodeList staticPartitions;
    private final SqlNodeList extendedKeywords;
    private final SqlNode targetTableID;
    private final SqlNodeList tableHints;
    @Nullable private final SqlLiteral conflictAction;

    public boolean isOverwrite() {
        return getModifierNode(RichSqlInsertKeyword.OVERWRITE) != null;
    }

    public Optional<SqlInsertConflictBehavior> getConflictStrategy() {
        if (conflictAction == null) {
            return Optional.empty();
        }
        return Optional.of(conflictAction.symbolValue(SqlInsertConflictBehavior.class));
    }
}
```

#### Step 5: SqlShowTablesの種別

```java
// SqlShowTables.java (28-91行目)
/**
 * SHOW TABLES sql call.
 * SHOW ( VIEWS | [ MATERIALIZED ]TABLES ) [ ( FROM | IN ) [catalog_name.]database_name ]
 * [ [NOT] LIKE <sql_like_pattern> ]
 */
public class SqlShowTables extends SqlShowCall {
    private final SqlTableKind kind;

    public enum SqlTableKind {
        MATERIALIZED_TABLE(new SqlSpecialOperator("SHOW MATERIALIZED TABLES", SqlKind.OTHER)),
        TABLE(new SqlSpecialOperator("SHOW TABLES", SqlKind.OTHER)),
        VIEW(new SqlSpecialOperator("SHOW VIEWS", SqlKind.OTHER));
    }
}
```

## 6. 関連機能・API

### 6.1 関連機能

| 機能名 | 関連種別 | 説明 |
|--------|---------|------|
| Table API Java（No.13） | 前提機能 | TableEnvironmentからの呼び出し |
| SQLプランナー（No.19） | 後続処理 | 解析結果の実行計画生成 |
| SQLクライアント（No.20） | 利用元 | SQL CLIからのSQL実行 |

### 6.2 使用例

```java
// TableEnvironmentからのSQL実行
TableEnvironment tEnv = TableEnvironment.create(...);

// DDL実行
tEnv.executeSql("CREATE TABLE my_table (id INT, name STRING) WITH (...)");

// クエリ実行
Table result = tEnv.sqlQuery("SELECT * FROM my_table WHERE id > 10");

// INSERT実行
tEnv.executeSql("INSERT INTO target_table SELECT * FROM source_table");

// STATEMENT SET
tEnv.executeSql("""
    BEGIN STATEMENT SET;
    INSERT INTO target1 SELECT * FROM source;
    INSERT INTO target2 SELECT * FROM source;
    END;
    """);
```

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

### 7.1 拡張性

| 観点 | 説明 |
|------|------|
| JavaCC文法 | Parser.jjで文法を拡張可能 |
| SqlNode継承 | 新しいDDL/DML/DQLを追加可能 |
| SqlConformance | SQL方言の切り替えが可能 |

### 7.2 パフォーマンス

| 考慮点 | 説明 |
|--------|------|
| 字句解析 | ストリーム処理で効率的にトークン化 |
| 構文解析 | LL(k)パーサーで線形時間解析 |
| メモリ | SqlNodeツリーのサイズに注意 |

### 7.3 制約事項

| 制約 | 説明 |
|------|------|
| Calcite依存 | Apache Calciteのバージョンに依存 |
| 識別子長 | デフォルト128文字まで |
| 予約語 | Flink固有の予約語に注意 |

## 8. 用語集

| 用語 | 説明 |
|------|------|
| SqlParser | Calciteの基本SQLパーサー |
| FlinkSqlParserImpl | Flink拡張を含むパーサー実装 |
| CalciteParser | SqlParserのFlink用ラッパー |
| SqlNode | SQL抽象構文木のノード |
| SqlCall | オペレータを持つSqlNode |
| SqlDdl | DDL文のSqlNode基底クラス |
| FlinkSqlConformance | FlinkのSQL準拠設定 |
| JavaCC | Javaパーサージェネレータ |
| AST | 抽象構文木（Abstract Syntax Tree） |
| RichSqlInsert | Flink拡張INSERT文 |
