result := length(value);
这样,不管是中文还是英文还是中英混合的字符串就都可求得正确的长度了。这就我至今仍百思不解的问题,为什么borland要绕个圈子通过指针去求参数值的长度呢?哪位朋友知道的话还请给我解释一下,非常感谢!
有些朋友可能会有疑问,为什么在不通过三层构架来做的时候不产生这个字符串被截断的问题呢?答案并不复杂,在直接通过adocommand来向sql server发送命令时,它是按表结构来决定参数长度的。它会先向sql server发一条
set fmtonly on select hth,gcmc from xshetong set fmtonly off
来获取表结构。而在三层构架下,tcustomadodataset内部虽然也是用tadocommand对象来发命令,但它却在取得表结构的后,并不用这个值来作为传参长度,而是重新去按实际参数来计算长度,结果就导致了错误。
bug2.clientdataset的lookup字段的问题:
bug再现的方法:
新建工程,在上面放置两个clientdataset,分别为cds1和cds2,它的数据来源任意,其中cds1为主数据集,在里面增加一个新的lookup字段,这个lookup字段根据cds1中的一个字符型的字段值到cds2中找出对应值来。
运行程序,一般来说是正常的,但是一旦cds1的被lookup字段中的值出现了一个单引号"'"(您可以修改或新增一条记录,输入单引号试试),立即会导致出错: unterminated string constant(未结束的字符串常量)。
bug分析与修复:
这个bug的产生原因要比上一个明显得多,一定是没有正确处理单引号带来的副作用引起的。
同样的,我们来跟踪vcl的源码:
运行程序,出错时打开call stack窗口(在view->debug windows)菜单中,查看函数调用情况,前面的一些调用是显而易见的,没有问题,我们从跟lookup有关的地方开始查原因,第一个与lookup有关的函数调用是tfield.calclookupvalue,我们在这个函数中设置断点,重新运行程序,中断下来后,进行单步调试。
tcustomclientdataset.lookup->tcustomclientdataset.locaterecord
经过上面的几次函数调用,很快的,我们就把目标定在了locaterecord过程中,在这个过程中,它根据lookup字段的设置情况,生成相应的过滤条件,然后到目标数据集中把对应的值找到,错就错在过滤条件的生成上了。比如,我们要按cds1中cust字段(假设是001)的值到cds2中按custid字段值找到对应的custname字段值。那生成的条件就应该是[custid] = '001',但如果cust的值是aa'bb,按生成的条件就会变成[custid] = 'aa'bb',显然导致了一个未结束的字符串常量。
通常我们解决单引号中又出现单引号的情况,只需把引号中的引号写两就行了,这里也是一样,只要让生成的条件变成[custid] = 'aa''bb'就不会出错了。所以可以这样修改源代码:
在locaterecord过程中找到下面的代码:
ftstring, ftfixedchar, ftwidestring, ftguid
if (i = fields.count - 1) and (lopartialkey in options) then
valstr := format('''%s*''',[vartostr(value)]) else
valstr := format('''%s''',[vartostr(value)]);
改成:
ftstring, ftfixedchar, ftwidestring, ftguid:
if (i = fields.count - 1) and (lopartialkey in options) then
valstr := format('''%s*''',[ stringreplace(vartostr(value),'''','''''',[rfreplaceall])])
else
valstr := format('''%s''',[ stringreplace(vartostr(value),'''','''''',[rfreplaceall])]);
也就是在生成过滤条件字符串时把条件的过滤值中的单引号全部一个变两。
为了确保这样修改的正确性,我查看了tcustomadodataset中的对应的locaterecord过程(在用tadodataset中的lookup字段时不会因单引号出错,只在用tcustomclientdataset时有这样的情况),它的处理方法与tcustomclientdataset稍有不同,它是通过getfilterstr函数来构造过滤条件的,但在getfilterstr中,它正确处理了单引号的问题。所以这样来看,没有在tcustomclientdataset的locaterecord中正确处理单引号的问题,确实是borland一个不大不小的疏漏。