Files
cobol-java-v3/docs/detailed-design/08-data-flow.md
T

30 KiB
Raw Blame History

数据流设计文档

版本: v1.0 | 日期: 2026-08-22 本文档描述 COBOL 迁移验证平台 V3 的完整数据流,覆盖非 DB(flat file)管道和 DBSQL/SQLite)管道。


1. 数据流概述

1.1 设计目标

目标 说明 实现方式
可追溯性 每条测试数据可追溯到源码决策点 路径约束 (field, op, value, want_true) 记录决策分支选择
一致性 非 DB 与 DB 管道共享同一套解析/生成引擎 cobol_testgen/ 统一提供 extract_structure + generate_data
容错性 单步失败不中断整体管道 每步返回 DbPipelineResult(success, data),失败立即终止后续
可观测性 全流程可监控、可调试 覆盖率报告、gcov 数据、JSON 中间产物、运行日志

1.2 管道对比

维度 非 DB 管道 DB 管道
编排器 orchestrator.py orchestrator_db.py
运行器 runners/cobol_runner.py runners/gixsql_runner.py
输入格式 固定长度平面文件 平面文件 + SQLite DB 种子
输出格式 平面文件 平面文件 + SQLite DB
验证方式 comparator/ 逐字段比对 Step 6 验证 COBOL/Java 输出一致性
步骤数 4 步(生成-运行-比对-报告) 6 步(环境-生成-运行-提取-Java-验证)

2. 非 DB 管道数据流

2.1 总体流程

COBOL 源码 (.cbl)
    |
    v  [输入阶段]
read.py: preprocess() + parse_data_division()
    |
    v  [生成阶段]
core.py: build_branch_tree() -> branch_tree + assignments
    |
    v
design.py: enum_paths() -> path_infos
    |
    v
design.py: make_base_record() -> base_record
    |
    v
design.py: apply_constraint() -> records (测试数据)
    |
    v  [运行阶段]
output.py: output_json() -> 测试数据 JSON
output.py: output_input_files() -> 固定长度平面文件
    |
    v
runners/cobol_runner.py: compile() + run() -> 输出文件
    |
    v  [比对阶段]
comparator/aligner.py: 按主键对齐 COBOL/Java 记录
comparator/field_compare.py: 逐字段比较
comparator/normalizer.py: EBCDIC/COMP-3 标准化
    |
    v
report/generator.py: 生成 HTML 报告

2.2 输入阶段

COBOL 源码 -> read.py 预处理 -> DATA DIVISION 解析 -> fields 结构信息

COBOL 源码 (fixed/free format)
    |
    v  read.preprocess()
    +-- resolve_copybooks(): 展开 COPY 语句
    +-- _is_fixed_format(): 自动检测 fixed/free 格式
    +-- 展开至每行 <=72 字符 (fixed) 或保持原样 (free)
    +-- 去除 EXEC CICS/SQL 块、逗号、ALL 关键字
    |
    v  预处理后源码 (preprocessed)
    |
    v  read.extract_data_division()
    +-- 提取 DATA DIVISION 文本块
    |
    v  read.parse_data_division()
    +-- Lark grammar.lark 解析 (Earley parser, dynamic lexer)
    +-- 逐行解析 FieldDef: level, name, PIC, USAGE, VALUE, REDEFINES, OCCURS
    +-- PIC 子句解析 -> PicInfo (type, digits, decimal, length, signed)
    +-- 返回 list[FieldDef]
    |
    v  expand_occurs()
    +-- OCCURS 展开为下标副本: WS-CELL(1), WS-CELL(2), ...

输出数据结构:

# fields: list[dict]  -- 每个字段一个 dict
{
    'name': 'R01EMP-ID',         # 字段名 (大写)
    'level': 1,                  # 层号 (01, 05, 77, 88)
    'pic': 'X(8)',               # PIC 子句原始文本
    'pic_info': {                # PIC 解析结果
        'type': 'alphanumeric',  # numeric | alphanumeric | alphabetic
        'digits': 0,             # 整数位数 (numeric)
        'decimal': 0,            # 小数位数 (numeric)
        'length': 8,             # 总长度 (alphanumeric)
        'signed': False,
    },
    'section': 'FILE',           # DATA DIVISION 节
    'occurs': 0,                 # OCCURS 次数
    'redefines': None,           # REDEFINES 目标
    'usage': None,               # COMP | COMP-3 | BINARY | DISPLAY
    'is_88': False,              # 是否 88 级条件
    'parent': None,              # 88 级父字段名
    'value': None,               # VALUE 子句
    'values': None,              # 88 级多值列表
    'is_filler': False,
}

2.3 生成阶段

fields -> core.py build_branch_tree -> design.py enum_paths -> make_base_record -> apply_constraint -> 测试数据

2.3.1 分支树构建

PROCEDURE DIVISION 文本
    |
    v  core.build_branch_tree_fallback()
    +-- pipeline_bridge: 3秒超时桥接
    |   +-- 优先: procedure_parser.py (新解析器, 行级状态机)
    |   +-- 回退: core._BrParser (旧解析器, 正则驱动)
    |
    +-- 段落扫描: scan_paragraphs() -> {name: (start, end)}
    +-- 逐段解析 IF / EVALUATE / PERFORM / READ / WRITE / MOVE / COMPUTE
    +-- 返回 (branch_tree: BrSeq, assignments: dict)

分支树节点类型:

节点 属性 说明
序列 BrSeq children: list 顺序执行的语句序列
条件 BrIf condition, cond_tree, true_seq, false_seq IF-ELSE 分支
评估 BrEval subject, subjects, when_list, other_seq EVALUATE 多分支
循环 BrPerform perf_type, condition, body_seq PERFORM UNTIL/VARYING
查找 BrSearch table_name, is_all, when_list SEARCH/SEARCH ALL
赋值 Assign target, source_info MOVE/COMPUTE/ADD/SUBTRACT/MULTIPLY/DIVIDE
调用 CallNode program_name, using_params CALL 子程序
跳转 GoTo target, body_seq GO TO
退出 ExitNode exit_type EXIT PARAGRAPH/PERFORM/PROGRAM

2.3.2 路径枚举

branch_tree + assignments
    |
    v  design_mcdc.enum_paths() / design.enum_paths()
    +-- 遍历分支树,收集每个决策点 (BrIf/BrEval/BrPerform)
    +-- 对每个决策点生成 True/False 或 WHEN 分支路径
    +-- MC/DC 约束集: cond.mcdc_sets() -> 每个叶条件的 T/F 独立约束
    +-- 路径约束合并: prior_false 累积(EVALUATE WHEN 入口条件)
    +-- PERFORM VARYING: 最后一次迭代约束 (last-iteration)
    +-- SEARCH: 表索引约束 + AT END 分支
    +-- 返回 path_infos: list[(constraints, path_assignments, term_type)]

路径约束格式:

# path_infos 的每个元素
(
    # constraints: Path = list[Constraint]
    [
        ('WRK-MONTH', '>', '0', True),       # (field, op, value, want_true)
        ('WRK-MONTH', '<', '13', True),
        ('R01EMP-ID', '<>', '00000000', True),
    ],
    # path_assignments: dict -- 赋值表
    {
        'WRK-PREV-EMP-ID': [{'type': 'move', 'source_vars': ['R01EMP-ID']}],
        'DBV-EMP-ID': [{'type': 'move_literal', 'literal': 'A0000001'}],
    },
    # term_type: str
    'normal'  # 或 'abend'
)

2.3.3 测试数据记录生成

path_infos + data_fields
    |
    v  design.generate_records()
    |
    +-- 遍历每条路径 (seq=1,2,3...):
    |   |
    |   +-- make_base_record(seq, data_fields)
    |   |   +-- 按 VALUE 子句设置初始值
    |   |   +-- 按 PIC 类型生成默认值:
    |   |   |   +-- numeric: _make_numeric_value(idx, seq, total_digits)
    |   |   |   +-- alphanumeric: _make_alpha_value(idx, seq, length)
    |   |   |   +-- date: seq_date(record_num) -> YYYYMMDD
    |   |   +-- 返回 base_record: dict {field_name: value}
    |   |
    |   +-- Pass A: propagate_assignments(rec, path_assign, data_fields)
    |   |   +-- 模拟赋值传播: MOVE/COMPUTE/READ INTO 等
    |   |
    |   +-- Pass B: apply_constraint(rec, field, op, val, want, ...)
    |   |   +-- trace_to_root(): 沿 MOVE 链追溯到根字段
    |   |   +-- invert_through_chain(): 反向求解约束值
    |   |   +-- satisfying_value(): 满足约束的值计算
    |   |   |   +-- numeric: 边界值 +/-1
    |   |   |   +-- alphanumeric: 字典序边界
    |   |   |   +-- date: YYYYMMDD 格式边界
    |   |   +-- 递归写入 base_record[field] = 满足值
    |   |
    |   +-- Pass B.5: forward propagate (变量间 MOVE 一致性)
    |   +-- Pass B.75: COMPUTE 重算 (约束修改源字段后)
    |   +-- get_term_type(path_cons) -> (filtered_cons, term_type)
    |
    +-- 返回 (records, kept_path_cons, term_types)

测试数据记录格式:

# records: list[dict] -- 每条记录一个 dict
[
    {
        'R01EMP-ID': 'A0000001',
        'R01DATE': '20250101',
        'R01LINE': '0001',
        'WRK-PREV-EMP-ID': '',
        'WRK-MONTH': '06',
        'DBV-EMP-ID': 'A0000001',
        '_assigned_fields': {'R01EMP-ID', 'R01DATE'},  # 内部标记
        '_w02_path': True,                              # PREV 连锁标记
    },
    # ... 更多记录
]

2.4 运行阶段

测试数据 -> output.py -> cobol_runner compile/run -> 输出文件

records + fd_fields + open_dir
    |
    v  output.output_json()
    +-- 按 FD 分组: field_to_fd 映射字段到 FD
    +-- 按 open_dir 确定方向: INPUT/OUTPUT/I-O
    +-- 按 roles 分类: input/inout -> input 块, output/inout -> expected_output 块
    +-- 不属于任何 FD 的字段 -> working_storage 块
    +-- 输出 JSON: {program, records: [{input, expected_output, working_storage, termination}]}
    |
    v  output.output_input_files()
    +-- 仅处理 INPUT/I-O 方向的 FD
    +-- 按 FD 分组,每 FD 输出一个 JSON: {stem}_{fd_name}.json
    +-- abend 记录单独输出: {stem}_abend_{fd_name}.json
    +-- 二进制模式: 按 field offsets 打包为固定长度二进制文件
    |
    v  flatfile.write_all_files()
    +-- analyze_fd_layout(): 解析 FD 记录布局 (字段名/PIC/offset/length)
    +-- 按 FD 布局序列化每条记录为固定长度字节
    +-- write_flat_file(): 输出到 outdir/{assign_name}
    |
    v  runners/cobol_runner.compile()
    +-- cobc -std=ibm -free -x src.cbl -o exe
    |
    v  runners/cobol_runner.run()
    +-- 设置 CWD,复制输入文件到 input/
    +-- subprocess.run(exe) 执行
    +-- 捕获 stdout/stderr,收集输出文件
    +-- 返回 RunResult(success, records, log)

2.5 比对阶段

输出文件 -> aligner.py -> field_compare.py -> normalizer.py -> report/generator.py -> HTML 报告

COBOL 输出文件 + Java 输出文件
    |
    v  comparator/aligner.py: align_records()
    +-- 按主键字段 (如 CUST-ID) 分组
    +-- 取两侧键的并集,按字符串排序
    +-- 逐键配对: MATCHED / MISSING_IN_SPARK / EXTRA_IN_SPARK
    +-- 返回 list[(cobol_record, java_record, status)]
    |
    v  comparator/normalizer.py: normalize_encoding()
    +-- EBCDIC -> ASCII 解码 (EBCDIC_037 映射表)
    +-- COMP-3 压缩十进制解码 (nibble -> decimal)
    +-- 日期格式标准化 (YYYYMMDD -> YYYY-MM-DD)
    |
    v  comparator/field_compare.py: compare_field()
    +-- 按字段类型选择比较策略:
    |   +-- numeric: Decimal 精度比较 + 容差 (tolerance=0.01)
    |   +-- date: YYYYMMDD 格式归一化后比较
    |   +-- string: strip 后直接比较
    +-- 返回 FieldResult(field_name, status, cobol_value, java_value)
    +-- status: PASS / MISMATCH / TOLERATED / NOT_SET
    |
    v  comparator/rounding_detect.py: detect_rounding()
    +-- 判断数值差异是否由 COBOL ROUNDED 子句引起
    +-- 计算舍入误差范围
    |
    v  report/generator.py: ReportGenerator
    +-- generate_json(): 输出 JSON 报告 (VerificationRun 数据)
    +-- generate_html(): 输出 HTML 报告
    |   +-- 覆盖率卡片: 段落覆盖率、分支覆盖率、决策点覆盖率
    |   +-- HINA 卡片: 判定类型、确信度
    |   +-- 质量评分卡片: quality_score
    |   +-- 重试历史卡片: heal_retry, simple_retry
    |   +-- 字段比对表格: PASS/MISMATCH 高亮
    +-- 返回报告文件路径

3. DB 管道数据流

3.1 总体 6 步流程

COBOL 源码 (含 EXEC SQL)
    |
    v  [Step 1: 环境整备]
step1_setup_environment()
    +-- 复制源码到 ASCII 工作目录
    +-- gixpp 预处理 + CONNECT TO 路径修补
    +-- cobc 编译 -> exe_path
    |
    v  [Step 2: 输入数据生成]
step2_generate_inputs(scenario)
    +-- extract_structure() -> 分支树 + 赋值表
    +-- generate_all_data() -> 测试数据记录
    +-- _init_database() + _populate_database() -> SQLite DB 种子
    +-- flatfile.write_all_files() -> 平面文件
    |
    v  [Step 3: COBOL 执行]
step3_run_cobol(scenario)
    +-- runner.run() -> 输出文件 + gcov 数据
    |
    v  [Step 4: 中间数据提取]
step4_extract_intermediate()
    +-- SELECT * FROM each table -> W01 JSON
    |
    v  [Step 5: Java 执行] (可选)
step5_run_java()
    +-- java -jar migration.jar -i W01.json -o output/
    |
    v  [Step 6: 验证]
step6_verify()
    +-- 比较 Java 输出与 COBOL 期望值
    +-- 返回 VerificationRun (PASS/MISMATCH)

3.2 Step 1: 环境整备 (step1_setup_environment)

职责: gixpp 预处理 + cobc 编译

cobol_src_dir/{program_id}.cbl
    |
    v  _copy_sources_to_workdir()
    +-- 复制主源码 {program_id}.cbl -> work_dir/src/
    +-- 复制 COPYBOOK (*.cpy) -> work_dir/src/
    +-- 复制子程序 (SUB*.cbl) -> work_dir/src/
        (搜索: cobol_src_dir, sub/, production/sub/, cobol-tna-system/sub/)
    |
    v  runner.preprocess(src, preprocessed/, copybook_dirs)
    +-- gixpp 预处理 + CONNECT TO 路径修补
    |   gixpp 错误转换: 'data/kin.db' -> 'sqlite://localhost/kin'
    |   修补为: 'sqlite:///{db_path}'
    |
    v  runner.compile(pp, exe, copybook_dirs, extra_srcs)
    +-- cobc 编译 -> work_dir/bin/{program_id}.exe
    +-- 编译日志写入 runtime_dir/logs/compile/
    |
    v  返回 DbPipelineResult(step=1, success, data={exe_path, log})

输入: cobol_src_dir, copybook_dirs, schema.subprograms 输出: src_path, pp_path, exe_path

3.3 Step 2: 输入数据生成 (step2_generate_inputs)

职责: COBOL 解析 -> 测试数据生成 -> DB 初始化 -> 平面文件输出

src_text + schema + scenario
    |
    v  COBOL 解析
    +-- extract_structure(src_text) -> 分支树 + 赋值表
    +-- generate_all_data() -> 测试数据记录 (白盒+机能+策略)
    +-- 后处理: R02APPL-ID 链接 R01APPL-ID
    |
    v  DB 初始化
    +-- 确定 DB 路径(场景分离: {program_id}_{scenario_id}.db
    +-- 清理旧 DB -> _init_database(db_path)
    |   +-- _create_tables(): 按 YAML schema 创建表 + 主键
    +-- _populate_database(): 注入种子行
    |   +-- 解析 COBOL -> 分支树 -> 路径枚举
    |   +-- build_db_input(): 生成 DB 输入行
    |   |   +-- collect_sql_meta(): 提取 SQL 元数据 (SELECT/INSERT/UPDATE/DELETE)
    |   |   +-- _hostvar_root(): WHERE 宿主变量 MOVE 链解析
    |   |   +-- _resolve_where_hostvar(): 宿主变量追溯到输入记录根字段
    |   |   +-- 用输入键建种子(与运行时一致)
    |   +-- 覆盖率驱动数据补充(日期、假期等)
    |   +-- 区间协调(INSURANCE-RATES / EMP-MASTER
    |   +-- INSERT OR IGNORE 写入 DB
    +-- _inject_extra_seed_rows(): 大结果集注入
    +-- _inject_sql_error_rows(): PK 冲突行注入
    |
    v  记录修补
    +-- records[0].R01EMP-ID = SPACE(触发空社员路径)
    +-- 全零 EMP-ID -> SPACE 清洗
    +-- 注入重复 EMP-IDAGG UPDATE 路径)
    +-- _deduplicate_r01_pk(): PK 去重
    +-- _inject_aggregation_boundaries(): 聚合边界数据
    |
    v  平面文件输出
    +-- write_all_files(): 全 FD 平面文件
    +-- write_sysin_file(): SYSIN 配置
    |
    v  返回 DbPipelineResult(step=2, data={records, flat_files, db_path})

输入: src_path, pp_path, schema, scenario 输出: generated_records, generated_structure, db_path, 平面文件

3.4 Step 3: COBOL 执行 (step3_run_cobol)

职责: 调用编译后的 COBOL 程序并收集 gcov 覆盖率数据

exe_path + schema + scenario
    |
    v  环境准备
    +-- 创建 runtime/run_{id}/main/{input,output}/ 目录
    +-- 复制生成的平面文件 -> input/
    +-- 复制 JSON -> json/
    |
    v  文件方向映射 (_scan_assign_to)
    +-- 正则扫描 SELECT/ASSIGN-TO -> {文件名: 方向}
    +-- OPEN 语句解析 -> INPUT/OUTPUT 方向确定
    |
    v  DB 路径准备
    +-- 场景 DB -> 复制到默认 DB 路径
    +-- CWD/data/kin.dbCONNECT TO 路径)
    |
    v  执行
    +-- 清理前次 .gcda 文件
    +-- runner.run(exe, cwd, db_path, env_overrides, command_args)
    +-- 日志写入 runtime_dir/logs/
    |
    v  gcov 数据收集
    +-- .gcda 从 CWD + exe_dir 复制到 gcov/run_{id}/
    +-- .gcno 同步(共享 .gcnoCOPY 不 MOVE
    |
    v  返回 DbPipelineResult(step=3, data={returncode, log, ...})

3.5 Step 4: 中间数据提取 (step4_extract_intermediate)

职责: 从 SQLite DB 导出 Java 程序所需的 JSON 中介数据

_current_db_path + schema.db_tables
    |
    v
    +-- 打开 DB
    +-- 遍历 schema.db_tables,对每张表执行 SELECT * FROM [table]
    +-- 构建 meta = {program_id, tables: {table_name: [rows]}}
    +-- 写入 work_dir/intermediate/{program_id}_W01.json
    +-- 返回 DbPipelineResult(step=4, data={tables, w01_path})

3.6 Step 5: Java 执行 (step5_run_java)

职责: 调用 Java 转换程序处理 COBOL 输出数据

java_input_path + java_jar
    |
    v
    +-- 创建 java_output 目录
    +-- 构建命令: java -jar {java_jar} -i {java_input_path} -o {java_out}
    +-- subprocess.run(cmd, capture_output=True, timeout=60)
    +-- 返回 DbPipelineResult(step=5, data={returncode, log})

3.7 Step 6: 结果验证 (step6_verify)

职责: 比较 Java 输出与 COBOL 期望值

java_output_path + _current_db_path
    |
    v
    +-- 构建 VerificationRun 结果对象
    +-- 读取 DB 各表行数(调试信息)
    +-- 扫描 java_output_path 下的 .txt/.json 文件
    +-- 设置 exit_code 和 statusPASS/MISMATCH
    +-- 返回 VerificationRun

4. 核心数据结构

4.1 fields 列表格式

fields 是整个数据流的核心数据结构,贯穿输入-生成-运行全流程。

# 类型: list[dict]
# 来源: read.parse_data_division() + expand_occurs()
# 用途: 分支树构建、路径枚举、约束应用、JSON 输出

[
    {
        'name': str,              # 字段名 (大写, 如 'R01EMP-ID')
        'level': int,             # 层号 (01, 05, 77, 88)
        'pic': str | None,        # PIC 子句原始文本
        'pic_info': {             # PIC 解析结果
            'type': str,          # 'numeric' | 'alphanumeric' | 'alphabetic' | 'unknown'
            'digits': int,        # 整数位数 (numeric)
            'decimal': int,       # 小数位数 (numeric)
            'length': int,        # 总长度 (alphanumeric)
            'signed': bool,       # 是否有符号
        },
        'section': str,           # 'FILE' | 'WORKING-STORAGE' | 'LINKAGE'
        'occurs': int,            # OCCURS 次数 (0=无)
        'occurs_depending': str | None,  # OCCURS DEPENDING ON 目标
        'redefines': str | None,  # REDEFINES 目标字段名
        'usage': str | None,      # 'COMP' | 'COMP-3' | 'BINARY' | 'PACKED-DECIMAL' | 'DISPLAY'
        'is_88': bool,            # 是否 88 级条件
        'parent': str | None,     # 88 级父字段名
        'value': str | None,      # VALUE 子句值
        'values': list[str] | None,  # 88 级多值列表
        'is_filler': bool,        # 是否 FILLER
    },
    # ... 更多字段
]

4.2 Constraint 约束格式

Constraint 是路径枚举和约束应用的基本单元。

# 类型: tuple (4 元组)
# 定义: models.py 中 Constraint = tuple
# 格式: (field, operator, value, want_true)

Constraint = (
    str,    # field: 字段名 (如 'WRK-MONTH', 'R01EMP-ID')
    str,    # operator: 比较运算符 ('=' | '<>' | '>' | '<' | '>=' | '<=' | 'not_in')
    str,    # value: 比较值 (字符串形式, 如 '12', '00000000', 'SPACE')
    bool,   # want_true: True=满足条件, False=不满足条件
)

# 示例:
('WRK-MONTH', '>', '0', True)        # WRK-MONTH > 0 为真
('R01EMP-ID', '<>', '00000000', True) # R01EMP-ID 不等于 00000000 为真
('SQLCODE', '=', '100', False)        # SQLCODE = 100 为假 (即 SQLCODE != 100)

4.3 Path 路径格式

Path 是一条完整执行路径的所有约束集合。

# 类型: list[Constraint]
# 定义: models.py 中 Path = list[Constraint]
# 含义: 所有约束同时满足时,程序沿该路径执行

Path = [
    ('WRK-MONTH', '>', '0', True),
    ('WRK-MONTH', '<', '13', True),
    ('R01EMP-ID', '<>', '00000000', True),
    ('__DP', '=', 'T', True),  # 决策点标记 (设计内部使用)
]

# path_infos 格式 (生成记录的输入):
path_infos = [
    # (constraints, path_assignments, term_type)
    (
        [Constraint, ...],         # 路径约束列表
        {str: list[dict]},         # 赋值表 (目标 -> 赋值操作列表)
        'normal' | 'abend',        # 终止类型
    ),
    # ... 更多路径
]

# 赋值表 (path_assignments) 格式:
{
    'WRK-PREV-EMP-ID': [
        {'type': 'move', 'source_vars': ['R01EMP-ID']}
    ],
    'DBV-EMP-ID': [
        {'type': 'move_literal', 'literal': 'A0000001'}
    ],
    'WS-COUNT': [
        {'type': 'compute', 'op': 'add', 'left': 'WS-COUNT', 'right': '1'}
    ],
}

4.4 测试数据 JSON 格式

测试数据 JSON 是非 DB 管道和 DB 管道共享的输出格式。

{
    'program': str,              # 程序名 (如 'KIN04CHK')
    'records': [
        {
            'input': {           # 按 FD 分组的输入字段
                'FD_NAME': {
                    'FIELD1': str,
                    'FIELD2': str,
                    ...
                }
            },
            'expected_output': {  # 按 FD 分组的期望输出字段
                'FD_NAME': {
                    'FIELD1': str,
                    ...
                }
            },
            'working_storage': {  # 不属于任何 FD 的工作存储字段
                'WRK-MONTH': str,
                'WS-COUNT': str,
                ...
            },
            'termination': str,   # 'normal' | 'abend'
        },
        # ... 更多记录
    ],
    'db_input': {                # DB 管道专用: DB 种子数据 (可选)
        'table_name': [
            {'col1': val1, 'col2': val2, ...},
            ...
        ]
    }
}

5. 数据流图

5.1 非 DB 管道完整流程 (Mermaid)

`mermaid flowchart TD A[COBOL 源码 .cbl] --> B[read.py preprocess] B --> C[read.py parse_data_division] C --> D[expand_occurs] D --> E[fields: list dict]

E --> F[core.py build_branch_tree]
F --> G[branch_tree + assignments]

G --> H[design.py enum_paths]
H --> I[path_infos]

I --> J[design.py generate_records]
E --> J
J --> K[records: list dict]

K --> L[output.py output_json]
L --> M[测试数据 JSON]

K --> N[output.py output_input_files]
K --> O[flatfile.write_all_files]
N --> P[固定长度平面文件]
O --> P

P --> Q[cobol_runner compile]
Q --> R[cobol_runner run]
R --> S[COBOL 输出文件]

S --> T[comparator/aligner align_records]
U[Java 输出文件] --> T
T --> V[记录对 pairs]

V --> W[comparator/normalizer]
W --> X[comparator/field_compare]
X --> Y[FieldResult list]

Y --> Z[report/generator generate_html]
Z --> AA[HTML 验证报告]

`

5.2 DB 管道完整流程 (Mermaid)

`mermaid flowchart TD A[COBOL 源码 含 EXEC SQL] --> B[Step 1: 环境整备] B --> B1[gixpp 预处理] B1 --> B2[cobc 编译] B2 --> B3[exe_path]

A --> C[Step 2: 输入数据生成]
B3 --> C
C --> C1[extract_structure]
C1 --> C2[generate_all_data]
C2 --> C3[records]

C3 --> C4[flatfile.write_all_files]
C4 --> C5[平面文件 input/]

C3 --> C6[build_db_input]
C6 --> C7[INSERT INTO SQLite DB]
C7 --> C8[db_path]

C5 --> D[Step 3: COBOL 执行]
C8 --> D
D --> D1[runner.run]
D1 --> D2[COBOL 输出文件]
D1 --> D3[gcov 数据]

D2 --> E[Step 4: 中间数据提取]
C8 --> E
E --> E1[SELECT * FROM tables]
E1 --> E2[W01 JSON]

E2 --> F[Step 5: Java 执行 可选]
F --> F1[java -jar migration.jar]
F1 --> F2[Java 输出文件]

F2 --> G[Step 6: 验证]
D2 --> G
G --> G1[VerificationRun]
G1 --> G2[PASS / MISMATCH]

`

5.3 数据流关键节点汇总

`mermaid flowchart LR subgraph 输入层 A1[COBOL 源码] A2[COPYBOOK] A3[YAML schema] end

subgraph 解析层
    B1[preprocess]
    B2[parse_data_division]
    B3[build_branch_tree]
end

subgraph 生成层
    C1[enum_paths]
    C2[generate_records]
    C3[build_db_input]
end

subgraph 输出层
    D1[output_json]
    D2[write_all_files]
    D3[write_sysin_file]
end

subgraph 执行层
    E1[cobol_runner]
    E2[gixsql_runner]
    E3[java -jar]
end

subgraph 验证层
    F1[aligner]
    F2[field_compare]
    F3[coverage]
    F4[report]
end

A1 --> B1 --> B2 --> B3
A2 --> B1
A3 --> C3
B3 --> C1 --> C2
C2 --> D1
C2 --> D2
C3 --> D2
C3 --> D3
D2 --> E1
D2 --> E2
D3 --> E2
E1 --> F1
E2 --> F1
E3 --> F1
F1 --> F2 --> F4
B3 --> F3 --> F4

`


6. 跨模块数据流转

6.1 模块间数据传递关系

源模块 目标模块 传递数据 数据格式
read.py core.py preprocessed, data_fields str, list[dict]
read.py design.py data_fields, open_dir, file_sec list[dict], dict, dict
core.py design.py branch_tree, assignments BrSeq, dict
cond.py design.py path constraints list[Constraint]
design.py output.py records, kept_path_cons, term_types list[dict], list, list[str]
design.py to_sql.py records, data_fields, assignments list[dict], list[dict], dict
output.py runner.py records (JSON/binary) dict, bytes
runner.py comparator/ output files file paths
to_sql.py orchestrator_db.py db_input (DB seed rows) dict[str, list[dict]]
flatfile.py orchestrator_db.py flat files (binary) file paths
orchestrator_db.py gixsql_runner.py exe_path, db_path, env Path, Path, dict
gixsql_runner.py orchestrator_db.py RunResult dataclass
comparator/aligner.py comparator/field_compare.py aligned pairs list[tuple]
comparator/field_compare.py report/generator.py FieldResults list[FieldResult]
coverage.py report/generator.py DecisionPoints, coverage rates list[DecisionPoint], float
data_merger.py __init__.py merged records list[dict]
hina/strategy.py data_merger.py strategy records list[dict]

6.2 核心数据流路径

路径 1: 非 DB 管道 (flat file)

read.py (fields) -> core.py (branch_tree) -> design.py (records)
-> output.py (JSON + flat files) -> cobol_runner (output files)
-> comparator/ (verification results) -> report/ (HTML)

路径 2: DB 管道 (SQL/SQLite)

read.py (fields) -> core.py (branch_tree) -> design.py (records)
-> to_sql.py (DB seed rows) -> flatfile.py (flat files)
-> gixsql_runner (output files + DB state)
-> step4 (W01 JSON) -> step5 (Java output) -> step6 (verification)

路径 3: 覆盖率收集

core.py (branch_tree) -> coverage.py (decision_points)
-> mark_coverage (covered branches) -> gcov.py (dynamic coverage)
-> coverage.py (merged coverage) -> report/ (HTML report)

6.3 关键中间产物

产物 生成位置 消费位置 格式
preprocessed read.py core.py, read.py str (预处理后源码)
data_fields read.py core.py, design.py, to_sql.py list[dict]
branch_tree core.py design.py, coverage.py BrSeq
assignments core.py design.py, to_sql.py dict
path_infos design.py design.py (generate_records) list[tuple]
records design.py output.py, to_sql.py, flatfile.py list[dict]
fd_fields read.py output.py dict[str, set[str]]
open_dir read.py output.py, flatfile.py dict[str, str]
file_sec read.py output.py, __init__.py dict[str, list[str]]
db_input to_sql.py orchestrator_db.py dict[str, list[dict]]
flat files flatfile.py runner.py file paths (binary)
JSON output.py runner.py, coverage.py file path
gcov data gcov.py coverage.py dict[int, int]
DecisionPoints coverage.py report/generator.py list[DecisionPoint]
VerificationRun comparator/ report/generator.py dataclass
FieldResult field_compare.py report/generator.py dataclass