sqle icon indicating copy to clipboard operation
sqle copied to clipboard

V2规则:禁止WHERE子句中条件字段与值的数据类型不一致,误触发

Open winfredLIN opened this issue 7 months ago • 1 comments

版本信息(Version)

版本:4.2502.0

问题描述与复现流程

  1. 建表
create table
  t1 (id bigint unsigned NOT NULL, name varchar(100));
  1. 使用SQL基于带有V2规则禁止WHERE子句中条件字段与值的数据类型不一致的规则模板审核
update t1
set
  name = 'jack'
where
  id = 2838923;
  1. 规则误触发

Image

问题原因

结论:在判断SQL中常量值类型的时候,值类型取值错误 具体原因:

  1. 在判断值的类型的时候,使用类型转换存在转换不准确,以及类型覆盖不全的问题
case types.KindInt64, types.KindUint64:
        return mysql.TypeLong, nil

Image 2. 在判断的时候严格要求类型枚举值相等,和1中产生矛盾 Image

解决方案

建议对值类型的数据,直接使用解析出的类型 Image

变更影响面

受影响的模块或功能

外部引用的潜在问题或风险

版本兼容性

测试建议

winfredLIN avatar May 20 '25 09:05 winfredLIN

修复方案

  • 修改获取SQL中常量值类型的逻辑:使用node.Type.Tp取代node.Datum.Kind()
  • 修改触发逻辑:当等式两边都是整数类型时,可以兼容不触发

测试影响面

  • 修复此 Bug 后,我把上边这条误触发的sql加入到了规则00112的测试中,全部通过。

huajianxiaowanzi avatar May 23 '25 02:05 huajianxiaowanzi

方案

规则目标:检查 WHERE/ON/USING 子句中条件字段与值的数据类型是否一致,避免隐式类型转换导致的性能问题。 【解析器的局限性】 当前使用的 SQL 解析器在判断常量类型时存在局限性:

  1. 数值常量类型判断不准确
    • 例如:常量 100 会被解析器判断为 TypeLong(BIGINT)
    • 实际上:100 可以是 TINYINT/SMALLINT/INT/BIGINT 等多种类型
    • 问题:如果严格按照解析器类型判断,会导致 WHERE id = 100(id为INT列)被误报
  2. 字符串常量类型判断
    • 短字符串可能被判断为 VARCHAR
    • 长字符串可能被判断为 TEXT
    • 实际上:字符串常量可以与 CHAR/VARCHAR/TEXT 类型的列兼容比较
  3. BLOB 常量判断
    • 解析器可能根据长度判断为不同的 BLOB 子类型
    • 实际上:BLOB 各子类型之间应该兼容 因此,本规则采用"大类匹配"和"范围检查"的策略:
  • 不完全依赖解析器给出的精确类型
  • 而是判断常量的值是否在列类型的合理范围内
  • 只要 MySQL 转换常量值而非列,就认为兼容 核心判断原则:
  1. 【列与列比较】- 严格匹配原则
    • 两列的数据类型必须完全一致
    • 任何类型不一致都会报错
    • 示例:INT 列与 VARCHAR 列比较 → 报错
    • 适用场景: a) WHERE/ON 子句中的列与列比较:WHERE t1.col1 = t2.col2 b) USING 子句:JOIN ... USING (column_name) - 检查两表中同名列的类型是否一致
  2. 【列与值比较】- 宽松兼容原则
    • 判断依据:值的大类与列的大类是否一致,且 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 与整数:小数类型与整数类型不同
  3. 【USING 子句特殊处理】
    • USING (column_name) 语法用于 JOIN,表示用同名列进行连接
    • 检查逻辑:获取 USING 中指定列在所有关联表中的类型,如果存在类型不一致则报错
    • 示例: SELECT * FROM t1 JOIN t2 USING (id) → 检查 t1.id 和 t2.id 的类型是否一致 → 如果 t1.id 是 INT,t2.id 是 VARCHAR,则报错

winfredLIN avatar Oct 23 '25 08:10 winfredLIN