背景
PR #427 (PostgreSQL 一等公民)review 中,@rootdeng 指出 mate_skill_file.content 定义为 TEXT、当 skill 文件含 NUL 字符(0x00)时在 PostgreSQL 上插入会报 invalid byte sequence for encoding "UTF8": 0x00。
根因:PG 的 text/jsonb 列不接受 0x00,而 MySQL(utf8mb4 TEXT)和 H2(CLOB)能存 ——所以这类问题在默认 H2/MySQL 上从不暴露,只有 PG 升一等公民后才会撞上。
该 review 指出的那一列已在 PR #427 (commit 70b69a7 )修复:在 SkillFileService.applyBundleFiles 写库前剥除 NUL,并让两条安装 fetcher(Zip/Git)一致地跳过二进制文件。但这只是同一类问题的一个实例 ,本 issue 跟踪剩余暴露面,统一收口。
剩余暴露面
1. mate_skill.skill_content(SKILL.md)
同样是 PG text 列,内容来自相同的 fetcher。
Zip 路径的 SKILL.md 已被 ZipSkillFetcher.isLikelyBinary 覆盖(在同一个解压循环里过二进制检测)。
Git 路径的 SKILL.md 仍走单独的 GitSkillFetcher 第 ~82 行 Files.readString(skillMd),不经过 isLikelyBinary ,理论上同病。NUL(0x00)是合法 UTF-8 字节,readString 会原样保留 → PG 上 skill_content 插入失败。
风险:markdown 几乎不会含 NUL,实际触发概率低,但属于同类缺口。
2. TEXT→JSONB 的 JSON 列(PR #427 引入)
建议方案
统一在「外部内容进入 PG 文本/JSONB 列」的入口处净化 NUL,而非逐列打补丁:
skill_content :给 GitSkillFetcher 的 SKILL.md 读取路径补上和 bundle 文件一致的二进制跳过 / NUL 净化。
JSONB 列 :在 JSON 序列化写库的入口(或 JacksonTypeHandler 路径 / 写库前的统一钩子)剥除 NUL,并补一条 Testcontainers PG 用例覆盖「JSON 值含 NUL」。
可考虑抽一个共享的 stripNul(String) 工具,复用 PR feat(db): 把 PostgreSQL 做成一等公民(独立迁移树 + JSONB + 集成测试) #427 在 SkillFileService 里的实现。
验证
关联
背景
PR #427(PostgreSQL 一等公民)review 中,@rootdeng 指出
mate_skill_file.content定义为TEXT、当 skill 文件含 NUL 字符(0x00)时在 PostgreSQL 上插入会报invalid byte sequence for encoding "UTF8": 0x00。根因:PG 的
text/jsonb列不接受0x00,而 MySQL(utf8mb4 TEXT)和 H2(CLOB)能存——所以这类问题在默认 H2/MySQL 上从不暴露,只有 PG 升一等公民后才会撞上。该 review 指出的那一列已在 PR #427(commit 70b69a7)修复:在
SkillFileService.applyBundleFiles写库前剥除 NUL,并让两条安装 fetcher(Zip/Git)一致地跳过二进制文件。但这只是同一类问题的一个实例,本 issue 跟踪剩余暴露面,统一收口。剩余暴露面
1.
mate_skill.skill_content(SKILL.md)text列,内容来自相同的 fetcher。ZipSkillFetcher.isLikelyBinary覆盖(在同一个解压循环里过二进制检测)。GitSkillFetcher第 ~82 行Files.readString(skillMd),不经过isLikelyBinary,理论上同病。NUL(0x00)是合法 UTF-8 字节,readString会原样保留 → PG 上skill_content插入失败。2. TEXT→JSONB 的 JSON 列(PR #427 引入)
_json列升级为JSONB。jsonb比text更严:连转义的 NUL(0x00)都拒(unsupported Unicode escape sequence 0x00 cannot be converted to text)。config_json/metadata/headers_json等),同样会在 PG 上炸。建议方案
统一在「外部内容进入 PG 文本/JSONB 列」的入口处净化 NUL,而非逐列打补丁:
GitSkillFetcher的 SKILL.md 读取路径补上和 bundle 文件一致的二进制跳过 / NUL 净化。JacksonTypeHandler路径 / 写库前的统一钩子)剥除 NUL,并补一条 Testcontainers PG 用例覆盖「JSON 值含 NUL」。stripNul(String)工具,复用 PR feat(db): 把 PostgreSQL 做成一等公民(独立迁移树 + JSONB + 集成测试) #427 在SkillFileService里的实现。验证
PostgresE2EBaseTest(Testcontainers),对每个修复点加一条「写含 NUL 内容 → 断言落库成功且读回已净化」的用例。关联
mate_skill_file.content)