3 Commits

Author SHA1 Message Date
ThearchyHelios 53d510357a ENUM / SET 只认 MySQL 带括号的写法;PostgreSQL 的域记底下的类型
复核发现(C-enum-prefix-match):类型写法改成按方言编译以后,PostgreSQL 的枚举、域编译出来就是类型名本身,
而 _type_family、is_date_column、_is_enum 和码值检查里的 is_set 都按前缀 startswith(("ENUM", "SET")) 认:
- 叫 setup_status 的 PG 枚举被当成 MySQL 的 SET,列定义里这次没出现的取值不写进码值;
- 叫 settlement_amount 的数值域被认成文字,又被当成码值候选。

改法:
- catalog._MYSQL_ENUM 只认 ENUM( / SET((_mysql_enum_kind),四处都改用它;PG 的枚举一律靠探查记下的
  enum_values 认(column_family、_is_enum 本来就看它)。
- is_date_column 加 enum_values 参数:有取值列表的不算日期列。PG 枚举的类型名里碰巧带 DATE(update_reason)
  以前会被认成日期列;剖析这边(catalog_profile._is_date_column)已经传上。sqlcheck 里那一处归 W1-a,没动,
  不传时行为和原来一样。
- 探查(introspect.type_text、_enum_values)遇到 PG 的域(DOMAIN)记它底下的类型(域套域一路往下找):域名
  看不出存的是数字还是文字,名字里的词还会让类型大类、日期列、码值候选认错,模型照着写 SQL 也要知道底下是
  NUMERIC(12, 2) 还是 DATE。套在域里的枚举照样记下取值列表。升级前存的是 str(类型) 即「DOMAIN」,同样认不出。

测试:test_profile_dialect_units 加以 set、enum 开头的 PG 类型名、带取值列表的 PG 枚举码值、DOMAIN 编译成底下
类型的单测;改动前 8 条失败。
2026-10-16 12:06:24 +08:00
ThearchyHelios ad1092900c 剖析的关系覆盖率把对不上类型的键算作没对上;升级前旧说法的剖析备注不再让目录升版本
复核发现两处(C-relation-coverage-inflated、C-profile-note-rewording-bumps-catalog):

一、覆盖率虚高。key_literals 把父列是数字、子列取到的不是整数写法的文字('WALKIN'、'GUEST0')从覆盖率的
分母里拿掉了:散客代号占九成的一列只剩 1 个整数写法的键对上,就测成覆盖率 100%、升为「已核实」,模型的
db_schema、自查、冻结快照都会照这条错的关系写 join。基线上同一份数据是 proposed、覆盖率 3.1%。
- 整数主键里永远没有这些文字,它们照样是子表的键值,算作没对上、留在分母里:
  覆盖率 = 对上的 / (拼进 IN 的 + 按类型就对不上的)。全都不是整数写法时不发父表的核对查询,覆盖率 0%、
  「可能不是这条关系」,不再整条判「无法核对」。
- key_literals 改回 KeyLiterals(literals, unmatchable, unsafe):不能安全拼进 SQL 的值单独计数,不再用
  「取到的 − 拼进的 − 不同类型的」倒推('0012' 和 '12' 写成同一个字面量时会被错说成「类型不支持核对」)。
- 被指向列是日期时间的关系整条跳过,并且在发子表取值查询之前就跳过、不占这张表的查询预算;说明改成
  「各库的时间写法和精度不同,不核对这种关系的覆盖率」——子表一侧也是时间时,以前写的「两端类型不同」不照实。
- 备注里「另有 N 个键值和被指向列的类型不同,未计入」改成「其中 N 个不是整数写法,被指向列是数字,算作没对上」。
- 报告里覆盖率不足的那句「抽样覆盖率 N,保持推断」也改成「覆盖率 N」,和备注一种说法。

二、升级后第一次重新剖析,数据没变也给每张有剖析结论的表升一个版本。剖析备注改了四处说法(抽样覆盖率→
覆盖率、子表抽样→子表取了、(只看前→(按存储顺序只看前、按前 N 行统计→按存储顺序取前 N 行统计),
catalog.same_profile_note 没认成同一句,已发布工作流记下的目录版本对不上,之后每次正式运行都报
catalog.drift。
- same_profile_note 比较前先把这四处固定句式的旧写法换成现在的写法(_LEGACY_PROFILE_WORDING,句式带着前后文
  匹配,不会误换人写的原话);算相同时保留目录里的旧备注,旧说法等结论真变了、重写备注时才换掉。
- 起草时核对有效行过滤写在模型依据后面的那句「剖析核对:按前 N 行统计」同样只认那一句的旧写法。
- MySQL / MariaDB 上新加的排序规则说明、ENUM 列定义里没出现的取值、无效日期个数是新信息,这些备注照常
  升一次版本,要写进发版说明。

测试:test_catalog_profile 加文字键指向整数主键的端到端用例(去掉修复回跑:测成 verified、覆盖率 100%);
test_profile_dialect_units 加覆盖率分母、时间键不发查询、unsafe 计数的单测;test_catalog_semantics 加跨版本
用例(把剖析备注改回旧说法写进目录、同样的数据重新剖析,版本号和目录都不变;去掉规整回跑两条失败)。
2026-10-16 12:02:41 +08:00
ThearchyHelios ee7b8b2f97 剖析与探查的方言修正(C1)和没有真库时的方言验证(C2)
为什么:剖析是全站唯一主动查业务库的地方,以前只在守卫放行和字符串片段两层上测过,从没在真库上跑过。
本机四个容器(PG 16、MySQL 8.4、MariaDB 11、Oracle Free 23)实测,旧的 IN 列表写法在 PostgreSQL 上整数子列
对文字主键直接报 operator does not exist: character varying = integer,关系一条都核对不了。

C1 方言修正:
- IN 列表的字面量按被指向列的类型族写(key_literals):父列文字写 '12',父列数字只收整数写法的值,父列是时间、
  对不上的不拼并在备注里写「两端类型不同」;子表是 CHAR(n) 时去掉补齐的尾随空格。
- 列类型改存按方言编译的完整写法(VARCHAR2(20 CHAR)、NUMBER(10, 0)、ENUM('a','b')、TIMESTAMP WITH TIME ZONE),
  编译不了退回 str()。SQLAlchemy 把 Oracle 的 VARCHAR2 反射成通用 VARCHAR(容器实测),照数据字典写成 VARCHAR2。
  ENUM / SET 另记 enum_values;Oracle 小写带引号的表和列记 quote_name;各索引(含主键)的首列记进
  index_first_columns(下一波取值观察判断能不能对大表发窗口查询)。新探查的快照哈希会变,旧运行封存的不受影响。
- catalog._type_family 认 ENUM / SET 是文字、INTERVAL 不是数字,去掉引号里的取值再认;新增 column_family
  (有 enum_values 的是文字,PostgreSQL 枚举编译出来只有类型名);is_date_column 不再把 ENUM('date') 认成日期列。
- 码值候选:ENUM / SET 不看列名直接算候选,ENUM 列定义里这次没出现的取值也写进码值(含义待填);VARCHAR2(20 CHAR)
  的长度按 20 算;CHAR 码值去尾随空格;MySQL 上写明按排序规则分组、比较(可能不分大小写)。
- 统计信息估出的行数不超过整表统计上限时再跑一次有上限的计数确认,统计过期的大表不再被当成小表做整表统计。
- 日期列的最小值、最大值只看 0001-01-02 到 9999-12-31 之间的值,零日期、占位日期、PG 的 ±infinity 不计入,
  照实写「另有 N 个无效日期未计入」,日期范围里带 invalid 个数(界面显示)。
- 新增取值观察用的方言方法 date_literal / day_trunc / sample_from,以及按日行数、时间窗口里的取值分布、块采样三类
  查询的拼法(窗口上端截在 9999-12-31 之前,不让 infinity 多出一天)。
- 说明文字不再说「抽样」:大表看的是「按存储顺序取前 N 行」;设置项「抽样键值数」改叫「核对键值数」。

C2 验证:
- tests/test_profile_dialect_golden.py:五种方言 × 全部剖析和观察查询的完整 SQL 钉在 tests/fixtures/dialect/,
  sqlglot 逐条解析、过只读守卫,再用 qualify + annotate_types 核对 IN 两边类型族一致(附旧写法的对照)。
- scripts/dialect-it.sh(不进 CI):本机起四个容器,建虚构夹具(保留字列、Oracle 小写带引号的表、CHAR 码值、
  MySQL ENUM、零日期 / infinity / 占位日期、做旧的统计信息、超上限的大表、带索引的日期列),用 AgentLab 自己的
  探查和剖析代码跑全部查询,断言零报错、结论和手算一致。四种库都通过;MariaDB 插入时自己更新行数估算,统计信息
  做不旧,照实打印。把 IN 列表改回旧写法回跑,PG 上照样报出上面那条错误。跑完删容器、留镜像。
2026-10-16 10:25:28 +08:00