# 機能設計書 71-parsesql

## 概要

本ドキュメントは、Nim標準ライブラリのparsesqlモジュールの機能設計について記載する。本モジュールはSQLパーサーを提供し、SQL文字列を抽象構文木（AST）に変換する機能を実装している。

### 本機能の処理概要

parsesqlモジュールは、SQL文字列をパースして抽象構文木（AST）を生成する高性能なSQLパーサーである。PostgreSQL構文およびSQL ANSI標準をサポートしている。

**業務上の目的・背景**：データベースアプリケーションやORMフレームワーク、SQLインジェクション検出ツール、SQLフォーマッターなどにおいて、SQL文を構造的に解析する必要がある。本モジュールは、SQL文字列を抽象構文木に変換することで、プログラムによるSQLの検査・変換・整形を可能にする。なお、本モジュールは非推奨となっており、nimbleパッケージとしての使用が推奨されている。

**機能の利用シーン**：
- ORMフレームワークでのSQL生成・検証
- SQLインジェクション検出ツールの開発
- SQLフォーマッター/ビューティファイアの実装
- データベースマイグレーションツールでのSQL解析
- SQLクエリ最適化ツールの開発

**主要な処理内容**：
1. SQL文字列のレキシカル解析（トークン化）
2. トークン列の構文解析（パース）
3. 抽象構文木（SqlNode）の構築
4. ASTからSQL文字列への逆変換（レンダリング）

**関連システム・外部連携**：本モジュールは純粋なパーサーであり、データベースとの直接的な連携はない。streamsモジュール、lexbaseモジュールと連携する。

**権限による制御**：特になし。純粋な文字列処理機能のため、権限管理は不要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本モジュールはGUI/画面を持たないライブラリ機能 |

## 機能種別

パーサー処理 / AST操作 / 文字列変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | Stream / string | Yes | パース対象のSQL文字列またはストリーム | 空でないこと |
| filename | string | No | エラーメッセージ用のファイル名 | - |
| considerTypeParams | bool | No | 型パラメータ（VARCHAR(255)等）をASTに含めるか | デフォルトfalse |

### 入力データソース

- 文字列リテラル
- ファイルストリーム
- 文字列ストリーム（newStringStream経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| SqlNode | ref SqlNodeObj | SQL抽象構文木のルートノード |
| strVal | string | リテラルノードの文字列値 |
| sons | seq[SqlNode] | 子ノードのシーケンス |

### 出力先

- メモリ上のSqlNodeオブジェクト
- renderSqlによる文字列出力

## 処理フロー

### 処理シーケンス

```
1. パーサーの初期化（open）
   └─ ストリームを開き、最初のトークンを取得
2. トークン取得ループ（getTok）
   └─ 空白・コメントのスキップ、次のトークンを識別
3. 構文解析（parse）
   └─ トークン列を消費しながらASTノードを構築
4. AST構築完了
   └─ nkStmtList（文リスト）をルートとするツリーを返却
5. パーサーの終了（close）
   └─ ストリームをクローズ
```

### フローチャート

```mermaid
flowchart TD
    A[開始: parseSql呼び出し] --> B[SqlParser初期化]
    B --> C[open: ストリーム接続]
    C --> D[getTok: 最初のトークン取得]
    D --> E{トークン種別判定}
    E -->|tkEof| F[parse完了]
    E -->|その他| G[parseStmt: 文解析]
    G --> H{キーワード判定}
    H -->|SELECT| I[parseSelect]
    H -->|INSERT| J[parseInsert]
    H -->|UPDATE| K[parseUpdate]
    H -->|DELETE| L[parseDelete]
    H -->|CREATE| M[parseTableDef/parseTypeDef/parseIndexDef]
    I --> N[ASTノード追加]
    J --> N
    K --> N
    L --> N
    M --> N
    N --> O{セミコロン?}
    O -->|Yes| D
    O -->|No| F
    F --> P[close: パーサー終了]
    P --> Q[SqlNode返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-71-01 | PostgreSQL互換 | PostgreSQL構文およびSQL ANSI標準をサポート | 常時 |
| BR-71-02 | 大文字小文字非依存 | SQLキーワードの大文字小文字を区別しない | キーワード比較時 |
| BR-71-03 | 文字列リテラル連結 | 改行で区切られた連続する文字列リテラルを連結 | 文字列パース時 |
| BR-71-04 | ネストコメント | /* */コメントのネストをサポート | コメントスキップ時 |

### 計算ロジック

演算子優先順位（getPrecedence関数）:
- 優先順位6: `*`, `/`, `%`
- 優先順位5: `+`, `-`
- 優先順位4: `=`, `<`, `>`, `>=`, `<=`, `<>`, `!=`, `IS`, `LIKE`, `IN`
- 優先順位3: `AND`
- 優先順位2: `OR`
- 優先順位1: `BETWEEN`
- 優先順位0: ユーザー定義演算子

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本モジュールはデータベース操作を行わない |

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

本モジュールはSQLのパースのみを行い、実際のデータベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SqlParseError | 構文エラー | 不正なSQL構文 | エラーメッセージに位置情報を含めて例外送出 |
| tkInvalid | 無効トークン | 認識できない文字/シーケンス | エラー位置を報告 |

### リトライ仕様

パース処理はリトライ不可。構文エラー時は修正後に再実行が必要。

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

本モジュールはパーサーのみであり、トランザクション管理は行わない。

## パフォーマンス要件

- 高性能なパーサー実装
- lexbaseモジュールによるバッファリング
- 文字列連結の最適化

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

- SQLインジェクション検出ツールとして活用可能
- パーサー自体はユーザー入力を安全に処理（実行しない）
- Unstable APIであるため、本番利用には注意が必要

## 備考

- 本モジュールは非推奨（deprecated）となっており、`nimble install parsesql`でインストールして`pkg/parsesql`としてインポートすることが推奨されている
- Unstable APIのため、将来的な変更の可能性がある

---

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

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

### 推奨読解順序

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

まず、SQL ASTのノード種別とデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | parsesql.nim | `lib/pure/parsesql.nim` | TokKind列挙型（26-47行目）: トークン種別の定義 |
| 1-2 | parsesql.nim | `lib/pure/parsesql.nim` | SqlNodeKind列挙型（473-534行目）: ASTノード種別の定義 |
| 1-3 | parsesql.nim | `lib/pure/parsesql.nim` | SqlNodeObj（544-551行目）: ASTノードのオブジェクト定義 |

**読解のコツ**: SqlNodeKindはcase文で使用される列挙型。LiteralNodesセット（536-539行目）がリテラルノードを定義し、これらはstrValフィールドを持つ。それ以外はsonsフィールド（子ノード）を持つ。

#### Step 2: レキサー（スキャナー）を理解する

トークン化処理の流れを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | parsesql.nim | `lib/pure/parsesql.nim` | skip関数（144-183行目）: 空白・コメントのスキップ処理 |
| 2-2 | parsesql.nim | `lib/pure/parsesql.nim` | getTok関数（368-443行目）: トークン取得のメイン処理 |

**主要処理フロー**:
1. **144-183行目**: skip関数で空白、`--`コメント、`/* */`コメント（ネスト対応）をスキップ
2. **368-443行目**: getTokで文字種別に応じたトークン取得を分岐
3. **185-219行目**: getString関数で文字列リテラルを解析（エスケープシーケンス対応）
4. **313-341行目**: getNumeric関数で数値リテラルを解析（整数/浮動小数点/指数表記）

#### Step 3: パーサーを理解する

構文解析の中核処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | parsesql.nim | `lib/pure/parsesql.nim` | parseExpr関数（783-784行目）: 式の解析エントリーポイント |
| 3-2 | parsesql.nim | `lib/pure/parsesql.nim` | lowestExprAux関数（766-781行目）: 演算子優先順位による式解析 |
| 3-3 | parsesql.nim | `lib/pure/parsesql.nim` | parseSelect関数（1106-1189行目）: SELECT文の解析 |
| 3-4 | parsesql.nim | `lib/pure/parsesql.nim` | parseStmt関数（1191-1221行目）: 文種別の判定と分岐 |

**主要処理フロー**:
- **663-682行目**: getPrecedence関数で演算子優先順位を取得
- **727-764行目**: primary関数でリテラル・識別子・関数呼び出しを解析
- **1223-1231行目**: parse関数でステートメントリストを構築

#### Step 4: ASTレンダリングを理解する

ASTからSQL文字列への変換処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | parsesql.nim | `lib/pure/parsesql.nim` | SqlWriter型（1238-1241行目）: レンダリング用の内部構造体 |
| 4-2 | parsesql.nim | `lib/pure/parsesql.nim` | ra関数（1304-1533行目）: 再帰的なAST→文字列変換 |
| 4-3 | parsesql.nim | `lib/pure/parsesql.nim` | renderSql関数（1535-1539行目）: 公開API |

**主要処理フロー**:
- **1535-1539行目**: renderSqlがSqlWriterを初期化しra関数を呼び出す
- **1304-1533行目**: ra関数がノード種別に応じて再帰的にSQL文字列を構築

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

```
parseSql(input: string)
    │
    ├─ newStringStream(input)
    │
    ├─ open(SqlParser, Stream, filename)
    │      └─ lexbase.open(SqlLexer, Stream)
    │
    ├─ parse(SqlParser) -> SqlNode
    │      │
    │      └─ parseStmt(SqlParser, SqlNode)
    │             │
    │             ├─ parseSelect(SqlParser)
    │             │      ├─ parseExpr(SqlParser)
    │             │      │      └─ lowestExprAux / primary
    │             │      ├─ parseFromItem(SqlParser)
    │             │      └─ parseWhere(SqlParser)
    │             │
    │             ├─ parseInsert(SqlParser)
    │             ├─ parseUpdate(SqlParser)
    │             ├─ parseDelete(SqlParser)
    │             └─ parseTableDef(SqlParser)
    │
    └─ close(SqlParser)
           └─ lexbase.close(SqlLexer)
```

### データフロー図

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

SQL文字列     ───▶  SqlLexer (トークン化)
   │                      │
   │                      ▼
   │              Token列 (tkIdentifier, tkSelect, ...)
   │                      │
   │                      ▼
   │              SqlParser (構文解析)
   │                      │
   │                      ▼
   └──────────────▶  SqlNode (AST)  ───▶  renderSql() ───▶ SQL文字列
                          │
                          ▼
                    treeRepr() ───▶ ツリー表現文字列
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| parsesql.nim | `lib/pure/parsesql.nim` | ソース | SQLパーサーの実装 |
| lexbase.nim | `lib/pure/lexbase.nim` | ソース | レキサーの基底クラス |
| streams.nim | `lib/pure/streams.nim` | ソース | ストリーム入出力 |
| strutils.nim | `lib/pure/strutils.nim` | ソース | 文字列ユーティリティ |
| decode_helpers.nim | `lib/std/private/decode_helpers.nim` | ソース | エスケープシーケンス処理ヘルパー |
