Skip to content

fix(db): MySQL 结果列字符集未知时按 UTF-8 兜底解码,修复文本列显示为二进制 - #267

Merged
feigeCode merged 1 commit into
mainfrom
fix/mysql-unknown-result-charset
Sep 21, 2026
Merged

feigeCode merged 1 commit into
mainfrom
fix/mysql-unknown-result-charset

Conversation

@feigeCode

@feigeCode feigeCode commented Sep 21, 2026

Copy link
Copy Markdown
Owner

现象

连接某些 MySQL 兼容实现 / 中间代理时,结果网格把文本列显示成「二进制 · N B」:表结构、自定义 SQL 都会出现,UNION ALL 这类把字符串字面量和表列混在一起的查询尤其明显。

根因

  • 这类服务端在结果列元数据里不下发字符集collation_id = 0charset = unknown(正常 MySQL 给 255/45,character_set_results=binary 时给 63)。
  • crates/db/src/mysql/codec.rsdecode_charactercolumn_text_decoder 取不到 decoder 时一律降级为 DbValue::Binary,网格于是显示二进制 + hex。
  • 实际字节是合法 UTF-8,所以问题在解码降级,不在数据。
  • 表数据查询有 schema 纠偏(normalize_table_query_result)能救回这类列;但 information_schema 元数据、自定义 SQL、UNION ALL 结果不走这条纠偏,仍然显示二进制。

修复

字符集未知(collation 0 / 未映射)时按严格 UTF-8 兜底解码,判定复用 decode_untyped_bytes 已有的 is_valid_utf8_text(合法 UTF-8 且不含控制字符):

  • 能解出文本 → DbValue::Text
  • 解不出(非法 UTF-8、含控制字符)→ 仍保留二进制 sidecar 与 hex 显示
  • wire 上已声明 collation 63 的字节列、已声明字符集的列 → 行为不变

改动集中在 crates/db/src/mysql/codec.rs(+71 行,含 3 个回归测试)。

验证

真机链路:本机 MySQL 8.0.45 + 一个透明代理(只把列定义包里的 character_set 改写成 0,协议与数据都来自真实 MySQL)复刻现场。

  • 修复前:文本列进入 binary_cells,网格显示二进制
  • 修复后:表数据 / UNION ALL(字面量列 + 多分支 + 中文 IN 列表,即现场查询形态)/ information_schema 三条路径 binary_cells = 0,文本正常;000102FFFF00 这类真二进制仍是二进制
  • cargo test -p db --lib 全绿;cargo clippy -p db --all-targets 无新增告警

取舍

字符集未知的服务端上,内容是「可打印 ASCII」的真二进制列会显示为文本(此前显示二进制)。X'0102' 这类含控制字符的仍按二进制走;若要严格区分,可再按 BINARY_FLAG 收窄。

现象:连接某些 MySQL 兼容实现/代理时,结果列元数据里的 character_set 为 0
(collation_id=0、charset=unknown),decode_character 在取不到 decoder 时一律降级为
DbValue::Binary,文本列因此在网格里显示成「二进制 · N B」;表结构元数据、自定义 SQL 与
UNION ALL 结果不走 schema 纠偏,同样受影响,而字节本身是合法 UTF-8。

修复:字符集未知(collation 0 / 未映射)时用严格 UTF-8 兜底解码,与
decode_untyped_bytes 对无类型字节的判定保持一致(合法 UTF-8 且无控制字符)。
非法 UTF-8 或含控制字符的字节仍保留二进制 sidecar;wire 已声明 collation 63 的字节列仍走
wire-binary 分支;已声明字符集的列行为不变。

取舍:字符集未知的服务端上,内容是「可打印 ASCII」的真二进制列会显示为文本;这类字节此前
显示为二进制,属显示层面的取舍,若需严格区分可按 BINARY_FLAG 收窄。

验证:真机 MySQL 8.0.45 + 把列定义包 character_set 改写成 0 的透明代理复刻现场,表数据 /
UNION ALL / information_schema 三条路径的文本列均不再进入 binary_cells,X'000102FF' 这类真
二进制仍为二进制;cargo test -p db --lib 1311 passed(含 3 个新增 codec 回归测试);
clippy 对本文件无告警。
@feigeCode
feigeCode force-pushed the fix/mysql-unknown-result-charset branch from 2cc0bbb to d46a38d Compare September 21, 2026 08:36
@feigeCode
feigeCode merged commit 023536d into main Sep 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant