Skip to content

fix(db): 修复外部驱动 u64 cell 十进制文本编码导致 MySQL 无符号列整表读取失败 - #257

Closed
feigeCode wants to merge 2 commits into
mainfrom
fix/unsigned-cell-decimal-text-249
Closed

feigeCode wants to merge 2 commits into
mainfrom
fix/unsigned-cell-decimal-text-249

Conversation

@feigeCode

Copy link
Copy Markdown
Owner

Closes #249

Description

MySQL (text-protocol) drivers classify BIGINT UNSIGNED / INT UNSIGNED columns as u64 cells, but encode the value as a decimal string on the wire — {"type":"u64","value":"4"} — while the host protocol crate (extension_protocol::row::CellValue::U64) only accepted a JSON number. Deserializing the driver response therefore failed with

数据加载失败: query error: invalid extermal driver response: invalid type: string "4", expected u64

and the whole table could not be loaded. Issue #207 is the same root cause.

This PR makes the host tolerant at its wire boundary, so already-released drivers keep working without a driver update:

  • New crates/extension-protocol/src/cell_number.rs: readers for the i64 / u64 / f64 cell value. Serialization is unchanged (still a JSON number); deserialization now also accepts an equivalent decimal / float text. Decimal text is lossless on the host side (u64::MAX round-trips exactly).
  • Anything that cannot be interpreted ("abc", "4.5" for u64, a plain JSON object) still fails with a clear serde error — no silent downgrade to another type, so a really broken driver is not masked.
  • Handles serde_json's arbitrary_precision feature (enabled workspace-wide): floats and out-of-range integers arrive through serde_json's private number map (visit_map) instead of visit_f64 / visit_u64, so all three readers implement that branch too (otherwise a plain 1.5 would be misread as "an object was sent").
  • CellValue wires the readers onto its three numeric variants; the wire contract itself is unchanged for numbers.

How to Test

  1. cargo test -p extension-protocol — library (212 tests) and integration suites pass.
  2. cargo test -p db — 1302 library tests pass, 0 failed.
  3. cargo clippy -p extension-protocol -p db --all-targets — no new warnings on the touched files.
  4. Regression tests added:
    • cell_number unit tests: number vs decimal text, u64::MAX, out-of-range integer, non-numeric text, object payload.
    • row.rs wire-level test: text-encoded u64 / i64 / f64 cells, a nested array cell, and the plain-number form (guards against regression).
    • crates/db/src/ipc/connection.rs::cursor_fetch_accepts_unsigned_cells_encoded_as_decimal_text — replays the reported driver payload through CursorFetchOutputCellValue::U64 { value: 4 }DbValue::Unsigned("4").
    • Mutation check: removing the u64 reader makes both regression tests fail with the issue's exact error invalid type: string "4", expected u64.

Checklist

  • I have read the CONTRIBUTING document and followed the guidelines.
  • Reviewed the changes in this PR and confirmed AI generated code (If any) is accurate.
  • Passed cargo run for story tests related to the changes.
  • Tested macOS, Windows and Linux platforms performance (if the change is platform-specific)

feigeCode and others added 2 commits September 20, 2026 18:55
- extension-protocol 新增 cell_number 读取器:CellValue 的 i64 / u64 / f64
  `value` 序列化仍只发 JSON number,读取时接受等值的十进制 / 浮点文本;
  无法解释的文本("abc"、给 u64 传 "4.5")仍报错,不静默降级成别的类型
- 修 issue #249 / #207:MySQL 文本协议驱动按声明类型把 BIGINT UNSIGNED
  列还原成十进制文本({"type":"u64","value":"4"}),宿主严格按 number 解析
  时报 `invalid type: string "4", expected u64`,整张表都读不出来
- 三个读取器都处理 serde_json 的 arbitrary_precision 分支:该 feature 下浮点
  与超范围整数走私有 number map 进 visit_map,漏掉会把 1.5 误判成对象

验证:cargo test -p extension-protocol(212+ 全绿)、cargo test -p db(1302 全绿)、
cargo clippy -p extension-protocol -p db --all-targets 无新增告警;
摘掉 u64 读取器后 db 与 protocol 两个回归测试均复现 issue 原始报错。
@feigeCode

Copy link
Copy Markdown
Owner Author

驱动侧根因修复已提:feigeCode/navop-extensions#9 —— toCellForKind 的 u64 分支改发 JSON 数字(与同函数里原生 uint 家族一致),oceanbase 驱动 0.1.11 → 0.1.12。

本 PR 保留为宿主兼容层:容忍已发布的老驱动继续发十进制文本,两边可独立发布。

复现链路与影响面:invalid external driver response 在 Rust 侧只有 crates/db/src/ipc/client.rs:97 一处产出,JsonRpcClient 只被 ipc/plugin.rs(外部驱动插件)使用;u64 这个 kind 只由 typeKind 里「类型名含 unsigned」命中(MySQL 系专有),当前能产出它的只有 OceanBase 驱动。内置 MySQL(v0.17.0 起 manager.rs 已走 Rust MySqlPlugin)不经过 IPC,也不会产生该错误;#207 里报告者描述的 "mysql" 需要确认那条连接的实际类型/已装驱动。

@feigeCode

Copy link
Copy Markdown
Owner Author

撤回这个 PR:根因已在契约侧修掉,宿主不再需要为旧驱动做读取容错。

  • 驱动侧:navop-extensions#9(已合并且发布为 oceanbase 0.1.12),u64 cell 改为按宿主结构体字段类型发 JSON 数字,序列化契约恢复单一来源。
  • 宿主侧:改为「最低版本门」,见 fix(db): 外部驱动增加最低版本门,OceanBase 低于 0.1.12 时引导用户更新 #260DriverRequirement::Required 增加 minimum_version,OceanBase 低于 0.1.12 时不再放行连接,而是提示「需要安装或更新驱动(最低版本 0.1.12)」并一键从扩展市场更新(安装流程会校验市场版本 ≥ 最低版本)。

理由:宿主只判断「是否已安装」时不校验版本,而旧驱动既不会被自动更新、宿主也没有任何提示,用户只能看到 invalid external driver response: invalid type: string "4", expected u64 这种读不懂的报错。加版本门后,用户拿到的是一条可操作的提示,契约本身保持严格。

关联:#249(问题)、#207(同类反馈)、#260(本方案的实施 PR)。

@feigeCode feigeCode closed this Sep 20, 2026
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.

[Bug]: MySQL 返回字符串 "4",Rust 侧结构体定义为 u64

1 participant