lang
lang/sql/forbidden/no-implicit-conversion.md
medium
sql-no-implicit-conversion
禁隐式类型转换(VARCHAR ↔ INT),用 CAST 显式。Use when 写 SQL / 迁移脚本 / 评审涉及 `no-implicit-conversion` 的 PR。
castconvertimplicit禁隐式类隐式类型式类型转
paths
**/*.sqlpy/**/*.pybackend/**/*.py
SQL · 禁隐式类型转换
规则
WHERE / JOIN 条件中禁止依赖 MySQL 隐式类型转换。比较两侧必须类型一致,必要时显式 CAST 或 CONVERT。
反例
-- ❌ session_id 是 VARCHAR,传 INT
SELECT * FROM sessions WHERE session_id = 12345;
-- MySQL 隐式转换:可能使索引失效 + 结果不正确
-- ❌ JSON 字段比较
SELECT * FROM presentations WHERE data = '{"key": "value"}';
-- → 不会走 JSON_EXTRACT 索引
正例
-- ✅ 类型对齐
SELECT * FROM sessions WHERE session_id = '12345';
-- ✅ 显式转换
SELECT * FROM tasks WHERE CAST(payload->>'$.user_id' AS UNSIGNED) = 123;
-- ✅ JSON 用 JSON_EXTRACT + CAST
SELECT * FROM presentations
WHERE JSON_UNQUOTE(JSON_EXTRACT(data, '$.theme_id')) = 'theme_001';
为什么严重
- 索引失效:MySQL 决定如何执行 cast 时可能丢弃索引,全表扫描
- 结果不确定:精度丢失(
'01' = 1为真,'01abc' = 1也为真) - 跨数据库不一致:MySQL / PostgreSQL 隐式转换规则不同
Tortoise 中的体现
# ❌ session_id 是字符串字段,传入 int
sess = await Session.filter(session_id=12345).first()
# Tortoise 会自动 stringify,但日志里看不到,难以排查
# ✅ 调用方保持类型
sess = await Session.filter(session_id=str(12345)).first()
CI 检测
人工 review 重点关注。无可靠的纯静态规则。
自检
- [ ] WHERE / JOIN 两侧类型一致?
- [ ] JSON 字段查询用
JSON_EXTRACT并显式 CAST? - [ ] 索引字段类型与查询参数完全匹配?