mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 03:01:58 +00:00
5b18eab310
验收时审批卡把两边完全相同的日期区间标成了差异:_text_diff 逐行比两边 sqlglot pretty 的输出,pretty 按宽度决定 换不换行,两个合取的 WHERE 排成一行、三个的拆成三行,同一个条件在两边落在不同的行上。原文 SQL 都是单行时原文栏 一行都不标,这一栏是唯一逐行指出差在哪的地方,审核人会以为统计期也不一样。 现在逐行比之前两边按同一套排版写(_diff_lines / _line_generator,按方言给 sqlglot 的生成器加一处改动):WHERE、 HAVING、ON 的合取式每个条件一行,各层子查询里的也一样;条件本身是 OR 连起来的一律加括号(单独一个和与别的并列时 写法一样);ON 照 WHERE 的样子单独一行、条件缩进在下面(ON 里和 WHERE 里的同一个条件不当成同一行)。比的时候 不看条件前面的 AND(一边多出来的条件排在最前面时,另一边同一个条件只差在有没有 AND),列出来的仍是各自原样的行。 只动排版:判不判等价、差异说明不变(拿测试里的 SQL 对照语料按三种方言、有无表结构逐对比过,items、equivalent、 fallback、level 全部一致),sql_semantics、sql_equivalence 原有测试全过。 前端夹具 dual_check.json 里的 text_diff 是服务端的真实输出,按 test_dual_check_fixture 的办法重新生成(只变了 text_diff);check-verify 的对照行一节另查「只差结束日期时开始日期不标」「多出的两个条件各占一行」。
102 lines
6.2 KiB
Python
102 lines
6.2 KiB
Python
"""explain_sql_diff 的 text_diff(「规范化后不同的行」):两边相同的条件不因为换行的位置不同被标成差异。
|
|
|
|
2026-10 验收:双盲复算审批卡上,两边完全相同的日期区间被标成了差异——
|
|
+ orders.paid_date >= '2026-09-28' AND orders.paid_date <= '2026-10-04'
|
|
- orders.paid_date >= '2026-09-28'
|
|
- AND orders.paid_date <= '2026-10-04'
|
|
- AND orders.status <> 'refunded'
|
|
sqlglot 的 pretty 按宽度决定换不换行:两个条件放得下就排在一行,三个就拆成三行,同一个条件在两边落在不同的行上。原文
|
|
SQL 都是单行时原文栏一行都不标,这一栏是唯一逐行指出差在哪的地方,审核人会以为统计期也不一样。
|
|
|
|
现在逐行比之前两边按同一套排版写:WHERE、ON、HAVING 的合取式每个条件一行(嵌在子查询里的也一样),条件本身是 OR
|
|
连起来的加上括号;比的时候不看条件前面的 AND(一边多出来的条件排在最前面时,另一边同一个条件只差在有没有 AND 上)。
|
|
只动排版:判不判等价、差异说明(items)不变,sql_semantics、sql_equivalence 原有的测试照样全过。
|
|
|
|
表名、列名、日期、取值都是虚构的。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from app.data.sql_semantics import explain_sql_diff
|
|
|
|
LABELS = ("原流程", "盲算")
|
|
#: 包裹表、仓库表的结构(虚构):给了表结构才认列,同验收时双盲复算节点的调用
|
|
SCHEMA = {"tables": {
|
|
"parcels": {"columns": [{"name": "id", "type": "INTEGER"}, {"name": "fee", "type": "REAL"},
|
|
{"name": "sent_on", "type": "TEXT"}, {"name": "state", "type": "TEXT"},
|
|
{"name": "hub", "type": "TEXT"}, {"name": "depot_id", "type": "INTEGER"}]},
|
|
"depots": {"columns": [{"name": "id", "type": "INTEGER"}, {"name": "zone", "type": "TEXT"},
|
|
{"name": "kind", "type": "TEXT"}]},
|
|
}}
|
|
WEEK = "sent_on BETWEEN '2031-03-03' AND '2031-03-09'"
|
|
|
|
|
|
def diff(a: str, b: str):
|
|
return explain_sql_diff(a, b, "sqlite", schema=SCHEMA, labels=LABELS)
|
|
|
|
|
|
def test_the_same_date_range_is_not_marked_when_one_side_has_one_more_condition():
|
|
"""验收时的形状:原流程比盲算多一个排除作废的条件,两边的日期区间一样——只标多出来的那一个条件。"""
|
|
a = f"SELECT SUM(fee) AS week_fee FROM parcels WHERE {WEEK} AND state <> 'void'"
|
|
b = f"SELECT SUM(fee) AS total_fee FROM parcels WHERE {WEEK}"
|
|
got = diff(a, b)
|
|
assert got.text_diff == ("- AND parcels.state <> 'void'",)
|
|
# 只动排版:判定和差异说明照旧
|
|
assert not got.equivalent and not got.fallback and got.level == "optimizer"
|
|
assert [(i.aspect, i.text) for i in got.items] == [("filter", "原流程排除了 state = 'void' 的行和 state 为空的行,盲算没有")]
|
|
# 反过来比也一样,只是换到「+ 」那一边
|
|
assert diff(b, a).text_diff == ("+ AND parcels.state <> 'void'",)
|
|
|
|
|
|
def test_an_extra_condition_sorted_first_does_not_drag_the_shared_ones_along():
|
|
"""多出来的条件排在最前面:另一边同一个日期条件只差在前面有没有 AND,不算不同。"""
|
|
a = f"SELECT SUM(fee) FROM parcels WHERE {WEEK} AND hub = 'north'"
|
|
b = f"SELECT SUM(fee) FROM parcels WHERE {WEEK}"
|
|
assert diff(a, b).text_diff == ("- parcels.hub = 'north'",)
|
|
assert diff(b, a).text_diff == ("+ parcels.hub = 'north'",)
|
|
|
|
|
|
def test_two_conditions_on_each_side_mark_only_the_one_that_differs():
|
|
"""同一列上的区间只差结束日期(这种只给兜底说明,审核人全靠逐行对照):开始日期那一行两边一样,不标。"""
|
|
a = f"SELECT SUM(fee) FROM parcels WHERE {WEEK}"
|
|
b = "SELECT SUM(fee) FROM parcels WHERE sent_on BETWEEN '2031-03-03' AND '2031-03-10'"
|
|
got = diff(a, b)
|
|
assert got.fallback
|
|
assert got.text_diff == ("- AND parcels.sent_on <= '2031-03-09'", "+ AND parcels.sent_on <= '2031-03-10'")
|
|
|
|
|
|
def test_on_conditions_get_one_line_each_too():
|
|
"""LEFT JOIN 的 ON:ON 单独一行,条件各占一行,多出来的那个条件单独标出来。"""
|
|
a = "SELECT COUNT(*) FROM parcels p LEFT JOIN depots d ON d.id = p.depot_id AND d.kind = 'cold'"
|
|
b = "SELECT COUNT(*) FROM parcels p LEFT JOIN depots d ON d.id = p.depot_id"
|
|
assert diff(a, b).text_diff == ("- AND depots.kind = 'cold'",)
|
|
# 关联条件本身不同:只标那一行,ON 那一行两边一样
|
|
c = "SELECT COUNT(*) FROM parcels p LEFT JOIN depots d ON d.zone = p.hub"
|
|
assert diff(b, c).text_diff == ("- depots.id = parcels.depot_id", "+ depots.zone = parcels.hub")
|
|
|
|
|
|
def test_having_conditions_get_one_line_each_too():
|
|
a = "SELECT hub, SUM(fee) FROM parcels GROUP BY hub HAVING SUM(fee) > 10 AND COUNT(*) > 2"
|
|
b = "SELECT hub, SUM(fee) FROM parcels GROUP BY hub HAVING SUM(fee) > 10"
|
|
assert diff(a, b).text_diff == ("- AND COUNT(*) > 2",)
|
|
|
|
|
|
def test_conditions_inside_a_subquery_get_one_line_each_too():
|
|
a = ("SELECT COUNT(*) FROM parcels p WHERE p.state <> 'void' "
|
|
"AND EXISTS (SELECT 1 FROM depots d WHERE d.id = p.depot_id AND d.zone = 'east')")
|
|
b = ("SELECT COUNT(*) FROM parcels p WHERE p.state <> 'void' "
|
|
"AND EXISTS (SELECT 1 FROM depots d WHERE d.id = p.depot_id)")
|
|
[line] = diff(a, b).text_diff
|
|
assert line.startswith("- AND ") and line.endswith(".zone = 'east'")
|
|
|
|
|
|
def test_an_or_condition_reads_the_same_alone_and_among_others():
|
|
"""OR 连起来的条件和别的条件并列时要加括号,单独一个时以前不加:同一个条件两边写法不同,就被标成了差异。现在条件
|
|
本身是 OR 的一律加括号,两边写法一样;加括号不改意思。"""
|
|
either = "(hub = 'north' OR hub = 'south')"
|
|
a = f"SELECT COUNT(*) FROM parcels WHERE {either} AND state <> 'void'"
|
|
b = f"SELECT COUNT(*) FROM parcels WHERE {either}"
|
|
assert diff(a, b).text_diff == ("- parcels.state <> 'void'",)
|
|
c = "SELECT COUNT(*) FROM parcels WHERE (hub = 'north' OR hub = 'west') AND state <> 'void'"
|
|
assert diff(a, c).text_diff == ("- AND (parcels.hub = 'north' OR parcels.hub = 'south')",
|
|
"+ AND (parcels.hub = 'north' OR parcels.hub = 'west')")
|