sqle
sqle copied to clipboard
V2规则:禁止WHERE子句中条件字段与值的数据类型不一致,误触发
版本信息(Version)
版本:4.2502.0
问题描述与复现流程
- 建表
create table
t1 (id bigint unsigned NOT NULL, name varchar(100));
- 使用SQL基于带有V2规则
禁止WHERE子句中条件字段与值的数据类型不一致的规则模板审核
update t1
set
name = 'jack'
where
id = 2838923;
- 规则误触发
问题原因
结论:在判断SQL中常量值类型的时候,值类型取值错误 具体原因:
- 在判断值的类型的时候,使用类型转换存在转换不准确,以及类型覆盖不全的问题
case types.KindInt64, types.KindUint64:
return mysql.TypeLong, nil
2. 在判断的时候严格要求类型枚举值相等,和1中产生矛盾
解决方案
建议对值类型的数据,直接使用解析出的类型
变更影响面
受影响的模块或功能
外部引用的潜在问题或风险
版本兼容性
测试建议
修复方案
- 修改获取SQL中常量值类型的逻辑:使用node.Type.Tp取代node.Datum.Kind()
- 修改触发逻辑:当等式两边都是整数类型时,可以兼容不触发
测试影响面
- 修复此 Bug 后,我把上边这条误触发的sql加入到了规则00112的测试中,全部通过。
方案
规则目标:检查 WHERE/ON/USING 子句中条件字段与值的数据类型是否一致,避免隐式类型转换导致的性能问题。 【解析器的局限性】 当前使用的 SQL 解析器在判断常量类型时存在局限性:
- 数值常量类型判断不准确
- 例如:常量 100 会被解析器判断为 TypeLong(BIGINT)
- 实际上:100 可以是 TINYINT/SMALLINT/INT/BIGINT 等多种类型
- 问题:如果严格按照解析器类型判断,会导致 WHERE id = 100(id为INT列)被误报
- 字符串常量类型判断
- 短字符串可能被判断为 VARCHAR
- 长字符串可能被判断为 TEXT
- 实际上:字符串常量可以与 CHAR/VARCHAR/TEXT 类型的列兼容比较
- BLOB 常量判断
- 解析器可能根据长度判断为不同的 BLOB 子类型
- 实际上:BLOB 各子类型之间应该兼容 因此,本规则采用"大类匹配"和"范围检查"的策略:
- 不完全依赖解析器给出的精确类型
- 而是判断常量的值是否在列类型的合理范围内
- 只要 MySQL 转换常量值而非列,就认为兼容 核心判断原则:
- 【列与列比较】- 严格匹配原则
- 两列的数据类型必须完全一致
- 任何类型不一致都会报错
- 示例:INT 列与 VARCHAR 列比较 → 报错
- 适用场景: a) WHERE/ON 子句中的列与列比较:WHERE t1.col1 = t2.col2 b) USING 子句:JOIN ... USING (column_name) - 检查两表中同名列的类型是否一致
- 【列与值比较】- 宽松兼容原则
- 判断依据:值的大类与列的大类是否一致,且 MySQL 会转换值而非列
- 如果 MySQL 在比较时转换常量值(而非列),则不影响列的索引使用,不报错
- 如果 MySQL 需要转换列,则会导致索引失效,需要报错
- 适用场景:WHERE/ON 子句中的列与常量比较:WHERE column = 'value' 兼容场景(不报错): a) 整数类型之间:TINYINT/SMALLINT/INT/BIGINT 常量与列比较,只要数值在列范围内即兼容 b) 字符串类型之间:VARCHAR/CHAR/TEXT 常量与列比较,MySQL 会转换值,兼容 c) BLOB 类型之间:BLOB/TINYBLOB/MEDIUMBLOB/LONGBLOB 互相兼容 d) 字符串与 BLOB:VARCHAR/CHAR 常量与 BLOB 列比较,MySQL 会转换值,兼容 不兼容场景(报错): a) 跨大类比较:整数与字符串、整数与日期时间等 b) 字符串与日期时间:字符串常量与 DATE/DATETIME 列比较(强制使用显式转换函数) c) DECIMAL 与整数:小数类型与整数类型不同
- 【USING 子句特殊处理】
- USING (column_name) 语法用于 JOIN,表示用同名列进行连接
- 检查逻辑:获取 USING 中指定列在所有关联表中的类型,如果存在类型不一致则报错
- 示例: SELECT * FROM t1 JOIN t2 USING (id) → 检查 t1.id 和 t2.id 的类型是否一致 → 如果 t1.id 是 INT,t2.id 是 VARCHAR,则报错