深圳幻海软件技术有限公司 欢迎您!

数据库丨从MySQL数值隐式转换成了double型的测试点,值得学习

2023-02-28

一、背景在进行某项系统测试时,遇到选择部分保单更新为加急状态后,未选中的保单也同步更新成了加急状态。经过比对分析,发现是SQL查询在数据库设计为字符型的字段,SQL语句中用了数值型来查询时,查询结果结果会多了末尾两位不一致的值,如下图,100320201000195806搜出100320201000

一、背景

在进行某项系统测试时,遇到选择部分保单更新为加急状态后,未选中的保单也同步更新成了加急状态。

经过比对分析,发现是SQL查询在数据库设计为字符型的字段,SQL语句中用了数值型来查询时,查询结果结果会多了末尾两位不一致的值,如下图,100320201000195806搜出100320201000195812等值:

百因必有果,通过度娘,首先了解到原来在MySQL中,当操作符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容,即字符型字段与数值型值比较时,会进行隐式类型转换:都转换为数值型。

即,数据库字段值为字符型1234,查询条件为数值1234的时候,数据库字符型1234会转换为数值型1234,能够查询到对应值。

但为什么数值型100320201000195806除了搜索出100320201000195806还有100320201000195812这样的字符型结果?

二、问题根因

数值隐式转换成了double型,考虑数值可能出现溢出。根据DOUBLE 类型固定占用8个字节(64位),并且需要一位表示符号,11位表示指数,2的52次方-1:2^53 = 9007199254740992,当输入的数值大于9007199254740992时,存在数值溢出 。

也就是当数字超过16位以后,数据库不会报错,而是溢出后转为最接近的值入库,如下图:设置字段类型为double型,9007199254740993入库后显示为9007199254740992。

并且如下图测试,溢出入库的数据转化后有某种没明显规律的规则:

同理,取数据时,大于9007199254740992的数值也会发生转换,如下图,9007199254740993可以检索出9007199254740992的数值:

所以Bug产生过程为:18位数值类型 为double类型(1.003202010001958E+18),数据库查询时将business_no 变成(1.003202010001958E+18),数据库存的business_no值也根据隐式类型转换原则也转为了double类型。最终,过滤末尾两位的数值相等。

至此,对不同字符产生的隐式类型转换 和过程中转 double 型、数值溢出问题都有了更新的认知。

三、从表象 bug 展开的测试点思考

1. 反向思考

以上是字符型字段导入数值型的案例,因为字符型存的也是数值,因此在未超过 16位的查询上,不会有其他异常,但如果是数值型字段导入字符型字段呢?

如下图测试所示:

插入 id=0 的数据后,查询 id=a 可以查询出 id=0 的数据。

原来 mysql 的隐式转换,在 int 类型的字段传入字符串会截取从第一位 int 型开始到第一个非 int 型的值作为条件。因为 a 没有 int 值,所以等于 0,最终查询 id=a 可以查询出 id=0 的数据。

特别注意:如果传入字符串为 01e2 ,则可以查询出 100。

2. 针对隐式转换的 bug,之后如何优化用例测试点,做主动防范,提高测试覆盖率?

(1) 功能问题考虑:

表单校验:使用不同于数据库设计类型的数据来进行测试,如数据库数值型,则用字符型数据测试,字符型数据用 0 或&等数值运算符测试,从源头尽量过滤不一致。

对于非 web 录入的源头或者后台程序代码看不到的类型转换的问题,可以使用多个超过 16 位的临近数、以及根据隐式转换规则转换后值一致的字符串进行数据铺底,测试点包括所有涉及后台查询的场景。在我们保险软件测试中,因为单证号是非常重要的基本数据,因此特别要注意超过 16 位的保单号、投保单等单证的铺底准备。

除此外,关注数值统计、时间比较的业务场景,因为涉及时间和数值统计时经常需要函数进行转换,此时如果格式不一致也会发生隐式转换的问题:如date( a.publish_time ) >= date_sub( curdate(), INTERVAL 1 DAY )

Date 显示格式:YYYY-MM-DD;DateTime 显示格式:YYYY-MM-DD HH:mm:ss。

所以 2008-12-27 16:25:46.635 经过 dat(e )函数处理后的时间为:2008-12-27 ,date_sub时间值精确到秒,因此发生隐式转换后,date()会转成 2008-12-27 0:0:0,时间范围上变窄。

数据库存在NULL值时,需要特别关注。因为当两个参数至少有一个是 NULL 时,比较的结果也是 NULL。