test: cover between/hostvar/gcov-merge, class conditions, schema drop-tables, gixsql fixes

This commit is contained in:
hangshuo652
2026-08-09 17:43:16 +08:00
parent 273a3f8211
commit ff51bda962
22 changed files with 2559 additions and 0 deletions
@@ -0,0 +1,58 @@
"""源级 CURRENT TIMESTAMP → CURRENT_TIMESTAMP 归一化测试(T4)。
gixpp 把长 SQL 折行,可能把 DB2 的 `CURRENT TIMESTAMP` 拆成 `CURRENT TIMES` /
`TAMP` 跨两行,导致 pp 级同行正则漏掉 → 生成的 UPDATE 保留空格版,
SQLite 报 `near "TIMESTAMP": syntax error`UPDATE 成功分支(SQLCODE=0)不可达。
修复:在 gixpp 之前对源里的 EXEC SQL 块做归一化,使生成的 SQL 字面量拼接后
为 SQLite 合法的 CURRENT_TIMESTAMP。仅作用于 EXEC SQL 块,不影响其它文本。
"""
import sys, os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from runners.gixsql_runner import _normalize_current_timestamp
def test_normalize_inside_exec_sql_block():
src = (" EXEC SQL\n"
" UPDATE SALARY-RESULTS SET\n"
" UPDATED-AT = CURRENT TIMESTAMP\n"
" END-EXEC.")
out = _normalize_current_timestamp(src)
assert "CURRENT_TIMESTAMP" in out
assert "CURRENT TIMESTAMP" not in out
def test_normalize_multiple_exec_sql_blocks():
src = (" EXEC SQL\n"
" INSERT INTO T1 (UPDATED-AT) VALUES (CURRENT TIMESTAMP)\n"
" END-EXEC.\n"
" EXEC SQL\n"
" UPDATE T2 SET UPDATED-AT = CURRENT TIMESTAMP\n"
" END-EXEC.")
out = _normalize_current_timestamp(src)
assert out.count("CURRENT_TIMESTAMP") == 2
assert "CURRENT TIMESTAMP" not in out
def test_normalize_case_insensitive():
src = " EXEC SQL\n SELECT CURRENT timestamp FROM T\n END-EXEC."
out = _normalize_current_timestamp(src)
assert "CURRENT_TIMESTAMP" in out
def test_normalize_idempotent_on_already_underscore():
src = " EXEC SQL\n UPDATE T SET UPDATED-AT = CURRENT_TIMESTAMP\n END-EXEC."
out = _normalize_current_timestamp(src)
assert out == src
def test_normalize_does_not_touch_outside_exec_sql():
src = (" MOVE 'CURRENT TIMESTAMP' TO WS-LIT\n"
" EXEC SQL\n"
" UPDATE T SET UPDATED-AT = CURRENT TIMESTAMP\n"
" END-EXEC.")
out = _normalize_current_timestamp(src)
# 块内被替换,块外(数据字符串)保持不变
assert out.startswith(" MOVE 'CURRENT TIMESTAMP' TO WS-LIT\n")
assert "CURRENT_TIMESTAMP" in out
@@ -0,0 +1,99 @@
"""EXEC SQL 内 DB2 schema 限定表名(SCHEMA.TABLE)剥离测试。
gixsql 会把 `FROM SALARYDB.EMP-MASTER` 原样归一化为 `FROM SALARYDB.EMP_MASTER`
注入 GIXSQL SQL 字符串;SQLite 无 schema 对象,OPEN CURSOR 报
'no such table: SALARYDB.EMP_MASTER' → SQLCODE≠0 → EMP-OPEN 错误分支 → ABEND
主循环 2000MAJSOR 永不执行(SHA02MNC 覆盖率 66.7% 根因)。
修复:gixpp 前把 EXEC SQL 块内 FROM/INTO/UPDATE/JOIN 后的 SCHEMA.TABLE
剥掉限定符(通用规则,处理任意 schema 名,无程序硬编码)。
"""
import sys, os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from runners.gixsql_runner import (
_strip_schema_qualifiers,
_normalize_schema_qualifiers,
)
def test_from_schema_table_stripped():
src = (
" EXEC SQL\n"
" DECLARE C2 CURSOR FOR\n"
" SELECT EMP-ID, EMP-NAME\n"
" FROM SALARYDB.EMP-MASTER\n"
" ORDER BY EMP-ID\n"
" END-EXEC.\n"
)
out = _strip_schema_qualifiers(src)
assert "FROM SALARYDB.EMP-MASTER" not in out
assert "FROM EMP-MASTER" in out
def test_from_three_part_qualified_stripped():
src = "EXEC SQL SELECT A FROM DB2INST1.PAYROLL.TIMESHEET END-EXEC."
out = _strip_schema_qualifiers(src)
assert "FROM TIMESHEET" in out
assert "DB2INST1.PAYROLL" not in out
def test_update_schema_table_stripped():
src = "EXEC SQL UPDATE SALARYDB.EMP-MASTER SET DEPT-CODE = '01' WHERE EMP-ID = :X END-EXEC."
out = _strip_schema_qualifiers(src)
assert "UPDATE EMP-MASTER" in out
def test_into_schema_table_stripped():
src = "EXEC SQL INSERT INTO SALARYDB.EMP-MASTER (EMP-ID) VALUES (:X) END-EXEC."
out = _strip_schema_qualifiers(src)
assert "INSERT INTO EMP-MASTER" in out
def test_join_schema_table_stripped():
src = ("EXEC SQL SELECT A.ID FROM A "
"JOIN SALARYDB.EMP-MASTER B ON A.ID = B.EMP-ID END-EXEC.")
out = _strip_schema_qualifiers(src)
assert "JOIN EMP-MASTER B" in out
def test_column_alias_qualifier_untouched():
"""列限定(E.EMP-ID / B.DEPT-CODE,非表名位置)不得剥离。"""
src = ("EXEC SQL SELECT E.EMP-ID FROM EMP-MASTER E "
"WHERE E.DEPT-CODE = '01' END-EXEC.")
out = _strip_schema_qualifiers(src)
assert "SELECT E.EMP-ID" in out
assert "E.DEPT-CODE" in out
assert "FROM EMP-MASTER E" in out
def test_hostvar_into_untouched():
"""SELECT ... INTO :DBV-X(宿主变量,非 SCHEMA.TABLE)不得剥离。"""
src = "EXEC SQL SELECT EMP-NAME INTO :DBV-EMP-NAME FROM EMP-MASTER END-EXEC."
out = _strip_schema_qualifiers(src)
assert "INTO :DBV-EMP-NAME" in out
def test_unqualified_table_untouched():
src = "EXEC SQL SELECT EMP-ID FROM EMP-MASTER ORDER BY EMP-ID END-EXEC."
assert _strip_schema_qualifiers(src) == src
def test_wrapper_only_touches_exec_sql_blocks():
text = " MOVE 'FROM A.B' TO WS-X.\n"
assert _normalize_schema_qualifiers(text) == text
def test_wrapper_strips_inside_exec_sql_only():
text = (
" MOVE 'X' TO WS-A.\n"
" EXEC SQL\n"
" SELECT EMP-ID\n"
" FROM SALARYDB.EMP-MASTER\n"
" END-EXEC.\n"
" MOVE 'FROM C.D' TO WS-B.\n"
)
out = _normalize_schema_qualifiers(text)
assert "FROM SALARYDB.EMP-MASTER" not in out
assert "FROM EMP-MASTER" in out
assert "MOVE 'FROM C.D' TO WS-B" in out
+66
View File
@@ -0,0 +1,66 @@
"""SQLCODE 归一化注入测试(T3)。
gixsql+SQLite 对主键冲突返回 SQLCODE=-1555SQLITE_CONSTRAINT_PRIMARYKEY),
而 DB2 语义是 -803。在预处理后的 COBOL 中注入通用归一化代码,把
SQLITE 约束错误码映射为 -803,使 `IF SQLCODE = -803` 分支可达。
"""
import sys, os
from pathlib import Path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from runners.gixsql_runner import GixsqlCobolRunner
def _make_runner():
return GixsqlCobolRunner(gixpp_path="gixpp", lib_path="lib")
def _pp_text_with_if():
return (
" CALL \"GIXSQLEndSQL\"\n"
" END-CALL.\n"
" IF SQLCODE = 0\n"
" ADD 1 TO CUN-DB-INS\n"
" END-IF.\n"
" IF SQLCODE = -803\n"
" PERFORM 2000MAJSOR-UPD\n"
" END-IF.\n"
)
def test_normalize_injects_mapping_after_endsql(tmp_path):
"""GIXSQLEndSQL 后注入 SQLITE 约束错误 → -803 映射"""
r = _make_runner()
pp = tmp_path / "test_pp.cbl"
pp.write_text(_pp_text_with_if(), encoding="utf-8")
r._patch_sqlcode_normalize(pp)
out = pp.read_text(encoding="utf-8")
assert "SQLCODE = -1555" in out
assert "MOVE -803 TO SQLCODE" in out
assert "SQLCODE = -2067" in out
def test_normalize_mapping_before_if_sqlcode():
"""映射代码注入在 IF SQLCODE = -803 判断之前"""
r = _make_runner()
pp = Path(r'C:\Users\marye\AppData\Local\Temp\opencode\t3_pp.cbl')
pp.parent.mkdir(parents=True, exist_ok=True)
pp.write_text(_pp_text_with_if(), encoding="utf-8")
r._patch_sqlcode_normalize(pp)
out = pp.read_text(encoding="utf-8")
# 映射代码必须在第一个 IF SQLCODE 之前
mapping_idx = out.find("MOVE -803 TO SQLCODE")
if_idx = out.find("IF SQLCODE = -803")
assert mapping_idx != -1
assert if_idx != -1
assert mapping_idx < if_idx
def test_normalize_does_not_change_ok_path():
"""归一化不影响 SQLCODE=0 成功路径"""
r = _make_runner()
pp = Path(r'C:\Users\marye\AppData\Local\Temp\opencode\t3b_pp.cbl')
pp.write_text(_pp_text_with_if(), encoding="utf-8")
r._patch_sqlcode_normalize(pp)
out = pp.read_text(encoding="utf-8")
assert "IF SQLCODE = 0" in out