1 现象:问题描述
在XXX产品通过portal创建用户级别时,发生了创建失败的现象。测试部的同事发现,数据库中用户级别配置项产生了数据溢出。当配置项"最大联系列表成员数"的值大于32767时虽然级别创建修改会成功,但是会发生溢出;例如,当该配置项的值为32768时,数据库中存储的数据为-32768。
2 关键过程:根本原因分析
通过走读代码,发现了下面的相关代码:
//定义用户级别配置
struct LevelConf
{
TInt4 grade;
TInt4 maxGroupNum;
TInt4 maxGroupMemberNum;
TInt4 maxResourceListNum;
TInt4 maxResourceListMemberNum;
public:
LevelConf()
{
maxGroupNum = 0;
maxGroupMemberNum = 0;
maxResourceListNum = 0;
maxResourceListMemberNum = 0;
}
}
在增加用户级别的函数实现中有下面的代码:
cmd.Param( 1 ).setAsShort() = levelConf.grade;
cmd.Param( 2 ).setAsShort() = levelConf.maxGroupNum;
cmd.Param( 3 ).setAsShort() = levelConf.maxGroupMemberNum;
cmd.Param( 4 ).setAsShort() = levelConf.maxResourceListNum;
cmd.Param( 5 ).setAsShort() = levelConf.maxResourceListMemberNum;
数据库的定义如下:
CREATE TABLE T_XDMSLEVELCONF
(
GRADE INTEGER NOT NULL ,
MAXGROUPNUM INTEGER ,
MAXGROUPMEMBERNUM INTEGER ,
MAXRESOURCELISTNUM INTEGER ,
MAXRESOURCELISTMEMBERNUM INTEGER,
CONSTRAINT CC1138020758677 PRIMARY KEY ( GRADE)
) ;
通过查看SQLAPI接口类saAP中,发现只有下面的SQL参数设置函数:
bool &setAsBool();
short &setAsShort();
long &setAsLong();
double &setAsDouble();
而没有setAsInt()函数;
原因分析:
(1)由于SQLAPI中没有提供setAsInt()函数,为了安全起见,程序中调用了setAsShort()函数,没有调用setAsLong()函数;
(2)当程序调用setAsShort()时,将TInt4类型的数据转换成TShort类型,存入数据库,一旦TInt4类型的数据超过TShort能够表示的最大值(32767),setAsShort()就会将TInt4转换成一个负数存入T_XDMSLEVELCONF;
(3)当再次从T_XDMSLEVELCONF表中取出字段的值时,就得到了负数,导致了错误的产生;
3 结论:解决方案及效果
解决方案:
由于受到SQLAPI的限制,这里不能修改setAsShort()函数的使用,为了规避问题,我们增加用户级别的函数实现,对LevelConf的取值范围进行了限制:
const int MAX_LEVEL_CONF_GRADE = 1000;
const int MAX_LEVEL_CONF_GROUP_NUM = 1000;
const int MAX_LEVEL_CONF_GROUP_MEMBER_NUM = 1000;
const int MAX_LEVEL_CONF_RESOURCE_LIST_NUM = 1000;
const int MAX_LEVEL_CONF_RESOURCE_LIST_MEMBER_NUM = 1000;
if (levelConf.grade <= MAX_LEVEL_CONF_GRADE
&& levelConf.maxGroupNum<= MAX_LEVEL_CONF_GROUP_NUM
&& levelConf.maxGroupMemberNum<= MAX_LEVEL_CONF_GROUP_MEMBER_NUM
&&levelConf.maxResourceListNum<= MAX_LEVEL_CONF_RESOURCE_LIST_NUM
&&levelConf.maxResourceListMemberNum<= MAX_LEVEL_CONF_RESOURCE_LIST_MEMBER_NUM)
{
cmd.Param( 1 ).setAsShort() = levelConf.grade;
cmd.Param( 2 ).setAsShort() = levelConf.maxGroupNum;
cmd.Param( 3 ).setAsShort() = levelConf.maxGroupMemberNum;
cmd.Param( 4 ).setAsShort() = levelConf.maxResourceListNum;
cmd.Param( 5 ).setAsShort() = levelConf.maxResourceListMemberNum;
}
else
{
…//向用户报错数据越界
}
效果:
问题得到了解决。
4 经验总结:预防措施和规范建议
(1)尽量使得应用程序中变量的类型和数据库字段类型一致;
(2)如果不能保证应用程序中变量的类型和数据库字段类型一致,也要求在代码中对变量的取值范围进行限制,防止存取数据库时出现溢出;
(3)从数据库读取或者写入数据时,需要注意数据库字段的类型;
5 备注
由于SQLAPI的限制,使得上面的解决方案有些绕来绕去的感觉,但是这样做确实能够保证安全。
6 考核点
程序和数据库的交互。
7 试题
应用程序操作数据库时,对于应用程序中的变量和数据库中对应的相关字段,编码时正确的方法是:
(1)必须保证应用程序中变量的类型和数据库字段类型一致;
(2)如果不能保证应用程序中变量的类型和数据库字段类型一致,也要求在代码中对变量的取值范围进行限制,防止存取数据时溢出;
(3)从数据库读取或者写入数据时,不需要注意数据库字段的类型;
(4)只要在单元测试时应用程序中的变量和数据库中相应字段能够交互即可。
答案:(1,2)
阅读(518) | 评论(0) | 转发(0) |