docs: add comprehensive system analysis document (docs/system-analysis.md)

This commit is contained in:
hangshuo652
2026-07-15 22:23:02 +08:00
parent bf207c20f5
commit 296e762c99
+679
View File
@@ -0,0 +1,679 @@
# 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 测试"有两种不同实现 | 功能重复 |