34 KiB
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 门控导入(惰性依赖),允许核心管线在缺少可选依赖时正常工作。
关键注意事项
- OCCURS 展开只支持数值下标 —
WS-CELL(WS-IDX)这类变量下标在展开阶段不被支持,处理在apply_constraint中单独完成 - PREV 链硬编码 R01/W01 前缀 —
_chain_prev()只处理以R01和W01开头的字段,是特定于 KIN/ZAN 程序集的假设 - C01 程序集硬编码 —
SUB04CHK、WRK-CSV-*、C01CHKRRC等字段名在多个位置硬编码(_inject_c01_coverage_records、_rebuild_r01line_csv、generate_recordsPass B.13) _MAX_PATHS = 50000— 复杂程序可能达到上限导致路径多样性丢失;_cap_paths用 T/F 多样性保护策略做公平裁剪,但仍可能丢失分支- 算术约束启发式 —
_apply_arith_constraint()使用启发式(大/小值分配)而非精确求解器,复杂算术条件可能无法精确满足 propagate_assignments20 轮收敛 — 硬编码迭代上限,理论上自引用 COMPUTE 链可能不收敛- 88-level 假设 — 目标程序没有 88-level VALUE,但解析器仍支持(用于测试程序
ALLCMDS.cbl) - False 值语义 —
SET X TO FALSE对 88-level 值的取反仅支持单字符值(Y→N,N→Y) - pipeline_bridge 3 秒超时 — 新解析器通过守护线程超时回退,可能在高负载下误触发
- 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/
关键注意事项
- Agent2Data 设计被覆盖 — line 110-114: LLM 生成的 TestSuite 被 cobol_testgen 的数据覆盖,实际只用了 LLM 的结构框架
- 质量门循环 — 最多 4 次迭代,每次调用
incremental_supplement()补充未覆盖分支;达到quality_gate_branch_threshold或超限停止 - DB 管道不可达 —
main.py不连接 orchestrator_db.py,DB 程序必须通过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
关键注意事项
- 单文件 1334 行 — 是项目中最大的文件,承担了生成、编排、DB 生命周期管理、gcov 合并等多种职责。函数间共享
self.*状态,内聚但高耦合 - gixpp 中文路径 bug —
_copy_sources_to_workdir()将源码复制到无中文路径的工作目录(C:\Users\...含中文字符可能导致 gixpp 崩溃) - 多场景 gcov 合并 — gcov 数据跨
normal/collision/abnormal运行合并,但只累加不隔离,可能导致分支标记冲突 skip_jvm=True常态 — step5/step6 通常被跳过(Java 侧可能未实现)_scan_assign_to()正则多行感知 — 跨越多行的 ASSIGN TO 语句通过多行正则会处理,但OPEN方向扫描仅检查单行INSERT OR IGNORE使用 — 重复 PK 注入时使用INSERT OR IGNORE静默忽略失败,可能导致预期行未写入但无报错- 硬编码的 COBOL 字段名 —
WRK-R01-REC,R01INNFIL,WRK-EMP-ID,WRK-C01-*等在多个位置直接引用 - EMP-ID 打补丁 — R01LINE 记录的 EMP-ID 在生成后会被
orchestrator_db.py额外处理(line 340+),与 cobol_testgen 生成的初始值可能不一致
四、编译运行引擎 — runners/
职责概述
4 类 runner(COBOL 非 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 |
关键注意事项
- Runner ABC 未覆盖 COBOL runner —
CobolRunner和GixsqlCobolRunner的 API 与RunnerABC 不兼容,调用方必须知道具体类型 - gixsql_runner 源码规范化 — 预处理阶段做大量源码修改(CONNECT TO 转换、变量注入、FROM/INTO 重排、缺失数据变量补丁),这些修改是 per-program 级别的,耦合了特定程序的知识
- DLL 部署策略 —
GixsqlCobolRunner.run()搜索 DLL 的路径顺序:TEMP → lib_path → gixpp_dir → x86/gcc → 系统 PATH;libfmt.dll有独立的搜索逻辑 SparkJavaRunner.get_coverage()硬编码 — 返回固定的branch_rate=0.80,无论实际测试结果CobolRunner新旧两套 API — 旧 API(stdin/stdout 管道)和新 API(文件 I/O + 子程序链接)并存,由不同调用方使用- 退出码 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 支持的版本
关键注意事项
- 中文路径 Bug —
gixpp.exe在含中文的路径下崩溃,所有源码必须复制到纯 ASCII 路径 - DLL 部署 — 运行时需要 11+ 个 DLL 文件,
gixsql_runner.run()会尝试从 5 个位置搜索并复制到 exe 目录;缺少任一 DLL 都会导致启动失败 -K符号表 — 编译时链接 11 个GIXSQL*符号,这些符号名必须与libgixsql.dll导出的完全一致- CONNECT TO 转化为宿主变量 — 源码硬编码的
CONNECT TO 'mydb'被替换为CONNECT TO :WS-GIX-CONN,运行时通过环境变量注入实际路径,这是运行时配置与源码强耦合的典型例子 - SQL 语法兼容性 — gixpp 支持标准 ESQL 语法,但不支持所有 DB2 COBOL 的 esoteric 特性(如某些嵌套 SQL 结构)
- 多后端共存 — 虽然本项目仅使用 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 |
关键注意事项
- 配置源不统一 —
Config从aurak.toml加载,ProgramSchema从 YAML 加载,映射配置从另一个 YAML 加载。三个配置系统用不同格式 - 4 个程序无运行场景 — KIN02UPD/KIN03EXP/KIN06CLD/KIN09CSV 的
runs:为空,orchestrator_db.run_all()跳过多场景循环但 stpe2/step3 仍会运行 - 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 系统全程序验证 |
注意事项
- 测试结果预记录 —
test-report.json包含预存的结果,可能在某些脚本中用于跳过实际执行(回归风险) - 大量一次性脚本 — rXX 系列(
r4_cond_coverage.py到r16_vuln_review.py)很可能是开发阶段的探索性脚本,维护状态未知 - 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
from cobol_testgen import extract_structure, generate_data
st = extract_structure(src, copybook_dirs=[...])
recs = generate_data(src, st, copybook_dirs=[...])
# st["coverage"] 包含覆盖率统计
关键注意事项
- 两个非 DB 管线入口 —
main.py走 LLM 代理管线(Agent1→Agent2→Agent3),python -m cobol_testgen的行内管线不走 LLM。同一个"非 DB"语义有两种实现 - DB 自动路由是字符串检查 — 检测
EXEC SQL的方式是简单的字符串包含匹配,可能被注释中的EXEC SQL误触发 - 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 通过
RunnerABC 有多态,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 测试"有两种不同实现 | 功能重复 |