Files
cobol-java-v3/docs/system-analysis.md
T

680 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# COBOL → Java/Spark 迁移验证平台 v3 — 系统分析
---
## 一、系统概览
### 职责概述
COBOL 测试数据生成 + 迁移验证平台:解析 COBOL 源码,生成分支全覆盖的测试数据,并行运行 COBOL 和 Java/Spark 版本,逐字段比对输出,判定迁移正确性并生成验证报告。
### 两条平行管道
系统包含两条完全独立的管道,根据源码是否包含 `EXEC SQL` 自动路由:
| 维度 | 非 DB 管道 | DB 管道 |
|------|------------|---------|
| 入口 | `main.py``orchestrator.py` | `python -m cobol_testgen``orchestrator_db.py` |
| 适用程序 | 纯 flat file I-O 程序 | 含 EXEC SQL 的 DB 程序 |
| 编译器 | `cobc` 标准编译 | `gixpp` (ESQL 预处理) + `cobc -l gixsql` |
| 存储 | 二进制 flat file | SQLite + flat file |
| 验证 | 二进制字节比对 | SQLite 表行比对 + 中间 JSON |
| 覆盖率 | 静态分支覆盖 | gcov 动态覆盖(跨场景合并) |
### 目录结构总览
```
cobol-java-v3/
├── cobol_testgen/ ← 核心引擎(18 个 .py 文件)
├── orchestrator.py ← 非 DB 管道编排
├── orchestrator_db.py ← DB 管道编排(1334 行)
├── main.py ← 非 DB CLI 入口
├── config/ ← 配置系统(全局 + per-program YAML
├── runners/ ← 编译运行引擎(6 个 runner)
├── gixsql/ ← vendored ESQL 工具链
├── test-data/ ← 测试套件(61 个脚本)
├── docs/ ← 文档
│── README.md / SETUP.md ← 项目首页 + 搭建指南
```
---
## 二、cobol_testgen 核心引擎
### 职责概述
COBOL 源码解析器 + 测试数据生成器。四层架构(INPUT → CORE → CONDITION → DESIGN+ 两个正交层(OUTPUT, COVERAGE),依赖单向流动。
### 内部结构
```
cobol_testgen/
├── models.py (163 行)
│ 数据模型层,零内部依赖
│ ├── PicInfo — PIC 解析结果 (type, digits, decimal, length, signed)
│ ├── FieldDef — DATA DIVISION 字段定义
│ ├── BrSeq/BrIf/BrEval/BrPerform/BrSearch — 分支树节点类型
│ ├── Assign — 赋值语句 (MOVE/COMPUTE/SQL/INITIALIZE 等)
│ ├── CondLeaf/CondNot/CondAnd/CondOr — 条件表达式树节点
│ ├── Constraint — 约束元组 (field, op, value, want_true)
│ └── ProcParseResult — 解析结果容器
├── read.py (675 行) ──── INPUT 层
│ 预处理 + DATA DIVISION 解析
│ ├── preprocess() — 主预处理: COPYBOOK 展开, EXEC 剥离, 注释清除
│ ├── resolve_copybooks() — 递归 COPY 解析 (10 层深度保护)
│ ├── resolve_sql_includes() — EXEC SQL INCLUDE → COPY
│ ├── strip_exec_sql_from_data_div() — 从 DATA DIVISION 移除 SQL 块
│ ├── parse_data_division() — Lark Earley parser → list[FieldDef]
│ ├── parse_pic() — PIC 字符串 → PicInfo
│ ├── parse_file_control() — FILE-CONTROL → {filename: {assign, org, mode}}
│ ├── parse_file_section() — FILE SECTION → {FDname: [01-names]}
│ └── scan_open_statements() — OPEN 语句 → {filename: INPUT/OUTPUT/I-O}
├── core.py (2169 行) ──── CORE 层
│ PROCEDURE DIVISION 解析 + 数据流追踪
│ ├── build_branch_tree() — 主入口: 源码 → 分支树
│ ├── _BrParser (类) — 行级状态机解析器
│ │ ├── parse_seq() — 顺序语句序列
│ │ ├── _parse_if() — IF ... END-IF
│ │ ├── _parse_evaluate() — EVALUATE ... END-EVALUATE
│ │ ├── _parse_perform() — PERFORM (UNTIL/VARYING/TIMES/THRU/行内)
│ │ ├── _parse_call() — CALL ... USING
│ │ ├── _parse_search() — SEARCH / SEARCH ALL
│ │ ├── _parse_sql() — EXEC SQL (SELECT/INSERT/DELETE/UPDATE/DECLARE CURSOR/FETCH)
│ │ ├── _record_assignment() — MOVE/COMPUTE/ADD/SUBTRACT/MULTIPLY/DIVIDE
│ │ ├── _parse_initialize() — INITIALIZE
│ │ ├── _parse_string() — STRING
│ │ └── _parse_unstring() — UNSTRING
│ ├── propagate_assignments() — 赋值传播 (20 轮收敛循环)
│ ├── trace_to_root() — 追踪赋值链到根变量
│ ├── classify_field_roles() — 字段角色分类 (input/output/inout/unused)
│ └── extract_sql_assignments() — 提取 SQL 赋值
├── cond.py (413 行) ──── CONDITION 层
│ 条件表达式解析 + MC/DC 枚举
│ ├── parse_single_condition() — 单条件 → (field, op, value)
│ ├── parse_compound_condition() — AND/OR/NOT/括号 → 条件树
│ ├── evaluate_tree() — 条件树求值 (leaf→bool)
│ ├── collect_leaves() — 收集所有叶子节点
│ ├── mcdc_sets() — MC/DC 约束集生成
│ └── satisfying_value() — 计算满足条件的值
├── design.py (1875 行) ──── DESIGN 层
│ 路径枚举 + 记录生成
│ ├── enum_paths() — 递归树遍历路径枚举 (~500 行)
│ ├── make_base_record() — 基础记录生成 (初值)
│ ├── apply_constraint() — 单约束应用到记录 (~125 行)
│ ├── generate_records() — 主生成入口: base → propagate → constrain → sync
│ ├── sync_redefined_fields() — REDEFINES 字段同步
│ ├── apply_occurs_depending() — OCCURS DEPENDING ON 处理
│ ├── _rebuild_r01line_csv() — CSV 源字段重建
│ ├── _reconstruct_unstring_sources() — UNSTRING 源重建
│ └── _set_invalid_value() — 无效值注入 (CALL ABEND 触发)
├── design_mcdc.py (286 行)
│ 线性路径枚举 (O(2N) 替代 O(2^N),无爆炸风险)
│ └── enum_paths() — 每决策点一条 T + 一条 F
├── pipeline_bridge.py (189 行)
│ 新旧解析器桥接: BranchNode (procedure_parser) ↔ BrSeq 规范模型
│ └── build_branch_tree_fallback() — 新解析器优先 → 3s 超时回退到旧解析器
├── procedure_parser.py (610 行)
│ 可选的 PROCEDURE DIVISION 解析器 (行级状态机)
│ └── extract_branch_tree() — 源码 → BranchNode 树
├── output.py (213 行) ──── OUTPUT 层
│ JSON 输出 + 二进制输入文件生成
│ ├── output_json() — 按 FD 方向分 input/expected_output/working_storage
│ └── output_input_files() — 写入 flat file
├── coverage.py (1375 行) ──── COVERAGE 层
│ 决策点收集 + 覆盖标记 + HTML 报告 (中文)
│ ├── collect_decision_points() — 遍历分支树收集决策点
│ ├── mark_coverage() — 从路径标记覆盖
│ ├── _mark_if() / _mark_eval() / _mark_search() / _mark_perform()
│ ├── generate_html_report() — 中文 HTML 报告 (svg, 梯度)
│ ├── generate_coverage_index() — 中文覆盖率索引页
│ └── run_coverage() — 主覆盖入口
├── gcov.py (194 行) ──── GCOV 层
│ gcov 解析 + 运行时覆盖
│ ├── parse_cbl_gcov() — 解析 .cbl.gcov → {line: count}
│ ├── run_gcov() — 执行 gcov 子进程
│ └── mark_from_gcov() — 从 gcov 数据标记覆盖
├── to_sql.py (481 行) ──── SQL 层
│ SQL WHERE 约束解析 + DB 行生成 (仅 DB 管道)
│ ├── sql_extract_constraints() — WHERE 子句 → 约束列表
│ ├── collect_sql_meta() — 从 Assignments 收集 SQL 元数据
│ └── build_db_input() — 按分支路径生成 DB 输入行
├── file_io.py (268 行) ──── 工具层
│ COBOL 二进制 I/O (DISPLAY/COMP/COMP-3)
├── flatfile.py (327 行) ──── 工具层
│ FD 布局分析 + flat file 写入
├── runner.py (587 行) ──── 工具层
│ cobc 编译 + 运行 + 非 DB 验证
│ └── run_all() / run_and_compare()
├── data_merger.py (130 行) ──── 工具层
│ 白盒 + 功能 + 策略数据合并
├── __init__.py (~1300 行) ──── 门面层
│ CLI 入口 main() + 公开 API
│ ├── main() — CLI 入口 (argparse, DB 自动路由)
│ ├── extract_structure() — 公有 API: 静态分析
│ ├── generate_data() — 公有 API: 数据生成
│ ├── expand_occurs() — OCCURS 递归展开
│ ├── _chain_prev() — 跨记录 PREV 链同步
│ ├── _inject_c01_coverage_records() — C01 决策点注入
│ └── incremental_supplement() — 增量补充 (质量门)
└── __main__.py (4 行)
python -m cobol_testgen → main()
```
### 数据流
```
COBOL 源码
read.py:preprocess() ← COPYBOOK 展开, EXEC SQL 剥离, 注释清除
read.py:parse_data_division() ← Lark parser → list[FieldDef]
__init__.py:expand_occurs() ← OCCURS → 下标展开
core.py:build_branch_tree() ← PROCEDURE DIVISION → 分支树
design.py:enum_paths() ← 树遍历 → T/F 路径列表 (含约束)
design.py:generate_records() ← 路径 → 记录
├── make_base_record() ← 初值
├── propagate_assignments() ← MOVE/COMPUTE 传播 (20 轮收敛)
├── apply_constraint() ← 约束应用
├── sync_redefined_fields() ← REDEFINES 同步
└── _rebuild_r01line_csv() ← CSV 重建
output.py:output_json() ← JSON (input/expected/ws)
output.py:output_input_files() ← 二进制 flat file
coverage.py:run_coverage() ← HTML 覆盖率报告 (中文)
```
### 依赖关系
```
models.py ← 零依赖, 被所有模块依赖
├── read.py ← lark (外部 parser)
├── cond.py ← 纯 stdlib
├── core.py ← cond.py
├── procedure_parser.py ← 纯 stdlib
├── design.py ← models, cond, core
├── design_mcdc.py ← models, cond, design
├── pipeline_bridge.py ← models, procedure_parser, core
├── output.py ← file_io
├── coverage.py ← models, cond (optionally gcov)
├── gcov.py ← 纯 stdlib
├── to_sql.py ← 纯 stdlib
├── runner.py ← file_io (optionally gcov)
├── flatfile.py ← read, file_io
├── data_merger.py ← cobol_testgen (自身)
└── __init__.py ← 所有上层模块 + japanese_data (外部)
```
无循环依赖。`runner.py` / `gcov.py` / `to_sql.py` 通过 try/except 门控导入(惰性依赖),允许核心管线在缺少可选依赖时正常工作。
### 关键注意事项
1. **OCCURS 展开只支持数值下标**`WS-CELL(WS-IDX)` 这类变量下标在展开阶段不被支持,处理在 `apply_constraint` 中单独完成
2. **PREV 链硬编码 R01/W01 前缀**`_chain_prev()` 只处理以 `R01``W01` 开头的字段,是特定于 KIN/ZAN 程序集的假设
3. **C01 程序集硬编码**`SUB04CHK``WRK-CSV-*``C01CHKRRC` 等字段名在多个位置硬编码(`_inject_c01_coverage_records``_rebuild_r01line_csv``generate_records` Pass B.13
4. **`_MAX_PATHS = 50000`** — 复杂程序可能达到上限导致路径多样性丢失;`_cap_paths` 用 T/F 多样性保护策略做公平裁剪,但仍可能丢失分支
5. **算术约束启发式**`_apply_arith_constraint()` 使用启发式(大/小值分配)而非精确求解器,复杂算术条件可能无法精确满足
6. **`propagate_assignments` 20 轮收敛** — 硬编码迭代上限,理论上自引用 COMPUTE 链可能不收敛
7. **88-level 假设** — 目标程序没有 88-level VALUE,但解析器仍支持(用于测试程序 `ALLCMDS.cbl`
8. **False 值语义**`SET X TO FALSE` 对 88-level 值的取反仅支持单字符值(`Y→N`, `N→Y`
9. **pipeline_bridge 3 秒超时** — 新解析器通过守护线程超时回退,可能在高负载下误触发
10. **gcov PERFORM 判断**`mark_from_gcov()` 使用 `count > 1` 作为 PERFORM Enter 的启发式判断,不是精确判断
---
## 三、管道编排层
### 职责概述
两条管道的调度中枢。非 DB 管道(`orchestrator.py`)管理 9 阶段 LLM 驱动的验证流程;DB 管道(`orchestrator_db.py`)管理 6 步 ESQL 程序的编译→生成→运行→比对流程。
### 3.1 非 DB 管道 — `orchestrator.py` (203 行)
#### 内部结构
```
run_pipeline(cfg, cpath, cbl, java, map_path)
├── 1. Agent1Parser(llm).parse() — COPYBOOK → FieldTree (LLM)
├── 2. cobol_testgen.extract_structure() — 静态分析
│ cobol_testgen.generate_data() — 测试数据生成
├── 3. hina.classify_program() — 程序类型分类 (LLM)
│ hina.supplement() — 策略补充
├── 4. cobol_testgen.check_coverage() — 覆盖率检查
│ quality gate loop: — 质量门循环 (最多 4 次)
│ gate_check() → incremental_supplement()
├── 5. Agent2Data(llm).design() — LLM 测试设计(被 cobol_testgen 数据覆盖)
├── 6. DataWriter — 写入 COBOL 二进制 + JSON
├── 7. CobolRunner.compile()/run() — COBOL 编译运行
├── 8. JavaRunner.compile()/run() — Java/Spark 编译运行
├── 9. CobolBinaryReader → align_records → compare_field — 比对
├── 10. Agent3Diagnostic(llm).analyze() — LLM 诊断
└── 11. ReportGenerator — JSON + HTML + machine.json
```
#### 依赖关系
`agents/` (Agent1Parser, Agent2Data, Agent3Diagnostic, LLMClient) + `hina/` (classify, gate, supplement) + `cobol_testgen/` + `runners/` (CobolRunner, JavaRunner, DataWriter) + `comparator/` + `report/` + `storage/` + `data/` + `config/`
#### 关键注意事项
1. **Agent2Data 设计被覆盖** — line 110-114: LLM 生成的 TestSuite 被 cobol_testgen 的数据覆盖,实际只用了 LLM 的结构框架
2. **质量门循环** — 最多 4 次迭代,每次调用 `incremental_supplement()` 补充未覆盖分支;达到 `quality_gate_branch_threshold` 或超限停止
3. **DB 管道不可达**`main.py` 不连接 orchestrator_db.pyDB 程序必须通过 `python -m cobol_testgen` 入口运行
### 3.2 DB 管道 — `orchestrator_db.py` (1334 行)
#### 内部结构
```
class GixsqlOrchestrator
├── __init__() — 加载 Config + ProgramSchema + GixsqlCobolRunner
├── run_all() — 主入口: Step 1→6 顺序执行
│ │
│ ├── Step 1: step1_setup_environment()
│ │ ├── _copy_sources_to_workdir() — 复制到 ASCII-only 工作目录
│ │ └── GixsqlRunner.preprocess()+compile() — gixpp + cobc
│ │
│ ├── [For each scenario in schema.runs]:
│ │ │
│ │ ├── Step 2: step2_generate_inputs()
│ │ │ ├── cobol_testgen: extract_structure + generate_all_data
│ │ │ ├── _init_database() — CREATE TABLE
│ │ │ ├── _populate_database() — INSERT 覆盖分支数据
│ │ │ ├── _inject_sql_error_rows() — 重复 PK (EXIT HANDLER 覆盖)
│ │ │ ├── _seed_matching_monthly_rows() — MONTHLY_ABSENCE 预填充
│ │ │ ├── flatfile.write_all_files() — 物理文件输出
│ │ │ └── output_json() — JSON 输出
│ │ │
│ │ └── Step 3: step3_run_cobol()
│ │ ├── _scan_assign_to() — 扫描 ASSIGN TO 方向
│ │ ├── GixsqlRunner.run() — 执行 COBOL
│ │ └── .gcda/.gcno 收集
│ │
│ ├── Step 4: step4_extract_intermediate() — SQLite → W01 JSON
│ ├── Step 5: step5_run_java() — java -jar (可跳过)
│ ├── Step 6: step6_verify() — COBOL vs Java 比对 (可跳过)
│ │
│ └── (可选) generate_coverage_report()
│ └── _merge_multi_run_gcov() + run_coverage() + HTML
├── _populate_database() (114 行) — DB 行生成: build_db_input + INSERT
├── _create_tables() — CREATE TABLE IF NOT EXISTS
├── _inject_sql_error_rows() — 重复 PK 注入
├── _seed_matching_monthly_rows() — 预填充匹配行 (DP#27/#28)
├── _make_synthetic_error_rows() — 兜底空表处理
└── _merge_multi_run_gcov() — 多场景 gcov 合并
```
#### 依赖关系
`cobol_testgen` (几乎所有子模块: read, core, design, design_mcdc, coverage, gcov, to_sql, flatfile, output, data_merger) + `runners.gixsql_runner` + `config` (Config + ProgramSchema) + `data.diff_result`
#### 关键注意事项
1. **单文件 1334 行** — 是项目中最大的文件,承担了生成、编排、DB 生命周期管理、gcov 合并等多种职责。函数间共享 `self.*` 状态,内聚但高耦合
2. **gixpp 中文路径 bug**`_copy_sources_to_workdir()` 将源码复制到无中文路径的工作目录(`C:\Users\...` 含中文字符可能导致 gixpp 崩溃)
3. **多场景 gcov 合并** — gcov 数据跨 `normal/collision/abnormal` 运行合并,但只累加不隔离,可能导致分支标记冲突
4. **`skip_jvm=True` 常态** — step5/step6 通常被跳过(Java 侧可能未实现)
5. **`_scan_assign_to()` 正则多行感知** — 跨越多行的 ASSIGN TO 语句通过多行正则会处理,但 `OPEN` 方向扫描仅检查单行
6. **`INSERT OR IGNORE` 使用** — 重复 PK 注入时使用 `INSERT OR IGNORE` 静默忽略失败,可能导致预期行未写入但无报错
7. **硬编码的 COBOL 字段名**`WRK-R01-REC`, `R01INNFIL`, `WRK-EMP-ID`, `WRK-C01-*` 等在多个位置直接引用
8. **EMP-ID 打补丁** — R01LINE 记录的 EMP-ID 在生成后会被 `orchestrator_db.py` 额外处理(line 340+),与 cobol_testgen 生成的初始值可能不一致
---
## 四、编译运行引擎 — runners/
### 职责概述
4 类 runnerCOBOL 非 DB / COBOL DB / Java Native / Spark+ 1 个 DataWriter。仅两个 Java runner 继承 `Runner` ABC,两个 COBOL runner 是独立类,没有多态派发。
### 内部结构
```
runners/
├── runner.py (40 行) ← ABC + 共享数据类型
│ ├── class Runner (ABC)
│ │ ├── compile(source_dir) → BuildResult
│ │ ├── run(artifact, input, output) → RunResult
│ │ └── get_coverage(artifact) → CoverageReport
│ ├── class BuildResult — success, artifact_path, log
│ ├── class RunResult — success, records, log
│ └── class CoverageReport — branch_rate, verdict, detail
├── cobol_runner.py (115 行) ← 非 DB COBOL runner (独立类)
│ ├── compile(src, dialect, gcov) — 旧 API: 单文件编译
│ ├── run(binary, input_path, output_path) — 旧 API: 管道 I/O
│ ├── compile_with_links(src, work_dir, ...) — 新 API: 多文件+子程序
│ └── run_file_based(binary, run_dir, input_files) — 新 API: 文件 I/O
├── gixsql_runner.py (423 行) ← DB COBOL runner (独立类)
│ ├── class GixsqlCobolRunner
│ │ ├── preprocess() — gixpp ESQL 预处理 + COPY 展开 + 源码规范化
│ │ ├── compile() — cobc -x -K GIXSQL* -l gixsql
│ │ ├── run() — 部署 DLL + 执行 + SQLite
│ │ └── read_db_tables() — 运行后回读 SQLite 表
│ ├── class GixsqlBuildResult
│ ├── class GixsqlRunResult
│ └── class GixsqlTableData
├── native_java_runner.py (30 行) ← Java 本地 runner (继承 Runner)
│ └── compile() → mvn -B package
│ run() → java -jar
│ get_coverage() → JaCoCo (若有)
├── spark_java_runner.py (36 行) ← Spark runner (继承 Runner)
│ └── compile() → mvn package
│ run() → spark-submit
│ get_coverage() → 硬编码 0.80
├── data_writer.py (33 行) ← 数据序列化 (独立类)
│ ├── write_cobol_binary() — struct.pack 二进制
│ ├── write_spark_json() — part-00000.json
│ └── write_native_json() — JSON lines
└── __init__.py (31 行) — 门面导出
```
### 依赖关系
| Runner | 外部依赖 |
|--------|----------|
| `cobol_runner` | `cobc` 命令行 (GnuCOBOL) |
| `gixsql_runner` | `gixpp.exe` + `libgixsql.dll` + 额外 DLL 链 (`gixsql/lib/`) + `cobc` |
| `native_java_runner` | `mvn` + `java` |
| `spark_java_runner` | `mvn` + `spark-submit` |
| `data_writer` | `data.test_case.TestCase` / `SparkConfig` |
### 关键注意事项
1. **Runner ABC 未覆盖 COBOL runner**`CobolRunner``GixsqlCobolRunner` 的 API 与 `Runner` ABC 不兼容,调用方必须知道具体类型
2. **gixsql_runner 源码规范化** — 预处理阶段做大量源码修改(CONNECT TO 转换、变量注入、FROM/INTO 重排、缺失数据变量补丁),这些修改是 per-program 级别的,耦合了特定程序的知识
3. **DLL 部署策略**`GixsqlCobolRunner.run()` 搜索 DLL 的路径顺序:TEMP → lib_path → gixpp_dir → x86/gcc → 系统 PATH`libfmt.dll` 有独立的搜索逻辑
4. **`SparkJavaRunner.get_coverage()` 硬编码** — 返回固定的 `branch_rate=0.80`,无论实际测试结果
5. **`CobolRunner` 新旧两套 API** — 旧 APIstdin/stdout 管道)和新 API(文件 I/O + 子程序链接)并存,由不同调用方使用
6. **退出码 0 或 1 都算成功**`gixsql_runner.run()` 将退出码 1 也视为成功(DB 程序可能在非错误路径返回 1)
---
## 五、gixsql ESQL 工具链
### 职责概述
vendored 在项目 `gixsql/` 目录下的 MinGW 编译版 GixSQL 工具链,提供 ESQL 预处理(`gixpp.exe`+ 运行时库(`libgixsql.dll`),用于将含 `EXEC SQL` 的 COBOL 程序转换为标准 COBOL + CALL 语句。
### 内部结构
```
gixsql/
├── bin/
│ ├── gixpp.exe — ESQL 预处理器 (主入口)
│ ├── libgcc_s_dw2-1.dll — GCC 运行时
│ ├── libstdc++-6.dll — C++ 标准库
│ └── libwinpthread-1.dll — POSIX 线程
└── lib/
├── libgixsql.dll — 主运行时 (.dll)
├── libgixsql.a — 静态库
├── libgixsql.dll.a — MinGW import lib
├── libgixsql-sqlite.dll — SQLite 后端插件 (本项目使用)
├── libgixsql-mysql.dll — MySQL 后端
├── libgixsql-pgsql.dll — PostgreSQL 后端
├── libgixsql-oracle.dll — Oracle 后端
├── libgixsql-odbc.dll — ODBC 后端
├── libgcc_s_dw2-1.dll — GCC 运行时 (冗余)
├── libstdc++-6.dll — C++ 标准库 (冗余)
├── libwinpthread-1.dll — POSIX 线程 (冗余)
├── libiconv-2.dll — 字符编码转换
├── libintl-8.dll — gettext 国际化
├── zlib1.dll — 压缩
├── libfmt.dll — C++ fmt 库
├── libxml2-2.dll — XML 解析
├── libssl-3.dll — OpenSSL TLS
├── libcrypto-3.dll — OpenSSL 加密原语
├── liblzma-5.dll — LZMA 压缩
├── libpq.dll — PostgreSQL 客户端
└── libmariadb.dll — MariaDB 客户端
```
### 数据流
```
含 EXEC SQL 的 COBOL 源码 (.cbl)
gixsql_runner.preprocess():
1. EXEC SQL INCLUDE SQLCA → COPY SQLCA
2. CONNECT TO 'literal' → CONNECT TO :WS-GIX-CONN USER :WS-GIX-USR
3. 注入 WS-GIX-* 变量定义
4. Python 端展开所有 COPY 语句 (替代 cobc -E)
5. 清除注释,规范行首到第 8 列
6. 调整 SQL FROM/INTO 顺序 → _norm.cbl
7. gixpp -i _norm.cbl -o _pp.cbl ← ESQL → 纯 COBOL (CALL gixsql*)
gixsql_runner.compile():
cobc -x -L gixsql/lib/ -K GIXSQL* -l gixsql _pp.cbl → .exe
gixsql_runner.run():
环境变量 GIXSQL_DB_PATH → .exe 运行时通过 libgixsql-sqlite
操作 SQLite 数据库
```
### 依赖关系
- `gixpp.exe` — 无外部 PATH 依赖(但规避中文路径)
- `libgixsql.dll` + `libgixsql-sqlite.dll` — 运行时需要通过 COB_LIBRARY_PATH 或部署到 exe 目录加载
- GnuCOBOL 3.2.0 (GC32-BDB-SP1) — 需要含 SQLite 支持的版本
### 关键注意事项
1. **中文路径 Bug**`gixpp.exe` 在含中文的路径下崩溃,所有源码必须复制到纯 ASCII 路径
2. **DLL 部署** — 运行时需要 11+ 个 DLL 文件,`gixsql_runner.run()` 会尝试从 5 个位置搜索并复制到 exe 目录;缺少任一 DLL 都会导致启动失败
3. **`-K` 符号表** — 编译时链接 11 个 `GIXSQL*` 符号,这些符号名必须与 `libgixsql.dll` 导出的完全一致
4. **CONNECT TO 转化为宿主变量** — 源码硬编码的 `CONNECT TO 'mydb'` 被替换为 `CONNECT TO :WS-GIX-CONN`,运行时通过环境变量注入实际路径,这是运行时配置与源码强耦合的典型例子
5. **SQL 语法兼容性** — gixpp 支持标准 ESQL 语法,但不支持所有 DB2 COBOL 的 esoteric 特性(如某些嵌套 SQL 结构)
6. **多后端共存** — 虽然本项目仅使用 SQLite,但 gixsql 包包含了 MySQL/PostgreSQL/Oracle/ODBC 后端,DLL 部署时会复制所有发现的 DLL
---
## 六、配置系统 — config/
### 职责概述
两级配置:全局级(`Config` dataclass,从 `aurak.toml` 加载)和 per-program 级(`ProgramSchema`,从 `config/programs/*.yaml` 加载)。
### 内部结构
```
config/
├── __init__.py (73 行) ← 全局配置
│ └── class Config (dataclass, 21 字段)
│ ├── 项目: project_name, copybook_paths, dialect
│ ├── LLM: llm_model, llm_timeout, llm_cache_dir, max_llm_cost
│ ├── 覆盖率: coverage_default, branch_pass
│ ├── 比对: rounding_mode, tolerance
│ ├── 运行: runner_mode, spark_master, spark_input_format
│ ├── 质量门: quality_gate_mode, quality_gate_*_threshold, max_quality_retries
│ ├── GCOV: gcov_enabled, gcov_work_dir, gcov_threshold
│ └── gixsql: gixsql_path, gixsql_lib_path, gixsql_db_path, gixsql_compile_flags
│ └── from_toml() — 从 aurak.toml 加载
├── mapping.py (39 行) ← 字段映射
│ ├── class FieldMapping — cobol_field, java_field, type, precision, trim, format
│ └── class MappingConfig — program, dialect, field_mappings, redefines_strategy
│ └── from_yaml() / get_java_field()
└── program_schema.py (93 行) ← per-program DB schema
├── class ColumnDef — name, type, primary_key, nullable, default, cobol_field
├── class TableDef — name, columns[], create_if_missing, sql_name
├── class SysinDef — period, include_invalid_period, modes[]
├── class ScenarioDef — id, sysin, inject_duplicate_pk
├── class ProgramSchema — program_id, db_tables[], subprograms[], db_type, runs[]
│ └── from_yaml()
└── load_schema() — 从 config/programs/ 按 ID 加载
```
### Per-Program YAML 配置 (6 个 DB 程序)
| ID | DB 文件 | 表 | 子程序 | 场景数 |
|----|---------|-----|--------|--------|
| KIN02UPD | kin.db | LEAVE_RECORDS | SUB02MSG, SUB03END | 0 |
| KIN03EXP | kin.db | LEAVE_RECORDS, HOLIDAY_CALENDAR | SUB01DAT, SUB02MSG, SUB03END | 0 |
| KIN06CLD | kin.db | HOLIDAY_CALENDAR, EMP_MASTER | SUB02MSG, SUB03END | 0 |
| KIN08DBU | kin.db | DAILY_RECORDS, MONTHLY_ABSENCE | SUB02MSG, SUB03END | 3 |
| KIN09CSV | kin.db | DAILY_RECORDS, MONTHLY_ABSENCE | SUB02MSG, SUB03END | 0 |
| ZAN06UPD | OVERTIME.DB | ZANTBL01, ZANTBL02 | SUB01DAT, SUB02MSG, SUB03END, SUB04CHK, SUB05TIM | 3 |
### 关键注意事项
1. **配置源不统一**`Config``aurak.toml` 加载,`ProgramSchema` 从 YAML 加载,映射配置从另一个 YAML 加载。三个配置系统用不同格式
2. **4 个程序无运行场景** — KIN02UPD/KIN03EXP/KIN06CLD/KIN09CSV 的 `runs:` 为空,`orchestrator_db.run_all()` 跳过多场景循环但 stpe2/step3 仍会运行
3. **gixsql 配置翻倍**`gixsql_path``gixsql_lib_path` 有默认值(相对于项目根),但 `gixsql_compile_flags``Config``gixsql_runner` 中都有定义,可能不一致
---
## 七、测试套件 — test-data/
### 职责概述
61 个测试脚本,覆盖从分支覆盖单元测试到端到端 DB 管道的全栈验证。
### 关键测试脚本
| 脚本 | 职责 | 验证内容 |
|------|------|----------|
| `s15_coverage_verification.py` | 8 个合成 COBOL 片段的管线覆盖测试 | IF/AND/NESTED/EVAL/PERFORM/ELSE-IF/VARYING/NOT |
| `s25_per_program_report.py` | 全量 43 程序覆盖率报告 | 3178/3178 = 100% |
| `s26_regression_check.py` | 回归快速检查 | ALL 43/43 AT 100% |
| `s30_db_e2e.py` | ZAN06UPD 6 步 DB 管线的端到端验证 | 编译→生成→运行→提取→Java→比对 |
| `s19_final_bridge_test.py` | pipeline_bridge 桥接器验证 | 新旧解析器一致性 |
| `s21_cond_fix_verify.py` | 条件解析修复验证 | AND/OR/NOT 处理 |
| `s17_gcov_comparison.py` | gcov 覆盖率比对 | 运行时 vs 静态覆盖率一致性 |
| `s22_tna_e2e.py` | KIN/ZAN 程序端到端 | TNA 系统全程序验证 |
### 注意事项
1. **测试结果预记录**`test-report.json` 包含预存的结果,可能在某些脚本中用于跳过实际执行(回归风险)
2. **大量一次性脚本** — rXX 系列(`r4_cond_coverage.py``r16_vuln_review.py`)很可能是开发阶段的探索性脚本,维护状态未知
3. **benchmark-programs 目录** — 项目外也有独立的 README 文档集,用于 COBOL 语句/模式的基准测试,与核心管线关系较松散
---
## 八、入口点
### 职责概述
三个入口点,对应两条管道 + 程序化 API。
### 8.1 非 DB CLI — `main.py` (50 行)
```
python main.py --copybook COPY.cpy --cobol-src PROG.cbl --java-src PROG.java --mapping map.yaml
│ --runner native|spark
│ --coverage boundary|branch
│ --quality-gate-mode warn|off
│ --gcov
│ --dry-run
└─ orchestrator.run_pipeline(config, copybook, cbl, java, mapping)
```
### 8.2 DB CLI — `cobol_testgen/__init__.py:main()` (~1300 行中的 ~500 行 CLI 代码)
```
python -m cobol_testgen [--gcov] [--temp-dir DIR] prog1.cbl prog2.cbl ... [outdir]
├── 自动检测 EXEC SQL → GixsqlOrchestrator (DB 管道)
└── 无 EXEC SQL → 行内管线 (非 DB,类似但独立于 orchestrator.py)
```
### 8.3 程序化 API
```python
from cobol_testgen import extract_structure, generate_data
st = extract_structure(src, copybook_dirs=[...])
recs = generate_data(src, st, copybook_dirs=[...])
# st["coverage"] 包含覆盖率统计
```
### 关键注意事项
1. **两个非 DB 管线入口**`main.py` 走 LLM 代理管线(Agent1→Agent2→Agent3),`python -m cobol_testgen` 的行内管线不走 LLM。同一个"非 DB"语义有两种实现
2. **DB 自动路由是字符串检查** — 检测 `EXEC SQL` 的方式是简单的字符串包含匹配,可能被注释中的 `EXEC SQL` 误触发
3. **CLI 代码与门面层混合**`__init__.py` 同时承担 CLI 解析、公开 API 导出、核心逻辑(OCCURS 展开、PREV 链、C01 注入),职责过重
---
## 九、跨层关注点
### 9.1 硬编码的领域知识
系统中散布着对 KIN/ZAN 程序集的假设:
- `R01*` 前缀硬编码(`_chain_prev`, `_rebuild_r01line_csv`
- `W01*` 前缀硬编码(`_chain_prev`, `_rebuild_r01line_csv`
- `SUB04CHK`, `WRK-C01*`, `C01CHKRRC`, `WRK-CSV-*` — C01 相关硬编码
- `EMP-ID`, `APPL-ID`, `YEAR-MONTH` — KIN 领域字段名硬编码
- `gixsql_runner.py` 中的 KIN06CLD 缺失字段补丁
### 9.2 两条管道的隔离度
- 共享 `cobol_testgen/` 核心库,但使用方式不同(DB 管道直接导入子模块,非 DB 管道通过 `__init__.py` 门面)
- 没有共享的编排基类
- 两个 Java runner 通过 `Runner` ABC 有多态,COBOL runner 没有
- 配置系统有重叠(`Config.gixsql_*` 只在 DB 管道使用,但存在全局 Config 中)
### 9.3 已知不合理标注
| 位置 | 问题 | 类型 |
|------|------|------|
| `orchestrator_db.py` 1334 行 | 单文件承担生成/编排/DB/合并等多种职责 | 职责过重 |
| `__init__.py` 1300 行 | CLI 逻辑与核心 API 混合 | 职责过重 |
| `core.py` 2169 行 | 包含解析、数据流追踪、SQL 提取等多种不相关功能 | 职责过重 |
| `spark_java_runner.py:35` | `get_coverage` 返回硬编码 0.80 | 假覆盖率 |
| `gixsql_runner.py:237-247` | per-program 缺失数据变量补丁(KIN06CLD 特有) | 领域泄漏 |
| `Runner` ABC 未被 COBOL runner 实现 | 无多态派发,调用方必须 `isinstance` | 接口不完整 |
| `main.py``__init__.py:main()` 两个非 DB 入口 | 同样的"非 DB 测试"有两种不同实现 | 功能重复 |