关于 SAP ABAP 字符变量和字符串变量字符个数的一个知识点

使用 ABAP strlen 函数计算下列这 4 个字符和字符串变量中包含的字符个数。

大家先别急着滑动屏幕,先试着自己计算一下,看和标准答案是否有出入。也许大家觉得这些小的知识点没什么用,但笔者马上会分享一个我实际处理过的客户 incident,正是由于类似这种看似不起眼的小知识点没有留意,最后影响了项目进展。
正确答案,依次是:
2
1
19
17
逐一解释。
strlen( lv_s ) = 2
整型变量的值,整数 1,赋给字符串变量 lv_s, 这里发生一个隐式类型转换。
SAP 帮助文档里声明,整型变量赋给字符串变量时,如果整数为负数,则字符串变量末尾为"-";如果整数为正数,则字符串变量末尾为空白字符。换言之,当整型变量到字符串变量的隐式类型转换发生时,字符串变量末尾会多出一位,代表赋值源头的整型数的符号位。

lv_s 多出来的这个空白字符在调试器里看得很清楚,2000 正是空白字符的 16 进制编码。同时调试器里也能看到 lv_s 的字符串个数为 2.

strlen( lv_s2 ) = 1
和前一例相比,lv_s2 的复制操作没有出现隐式类型转换,而是直接被赋以了一个字符常量,故字符个数为 1.
strlen( lv_ss) = 19
lv_ss 的类型为 SSTRING,实际就是一个 CHAR20:

在调试器里,lv_ss 有 18 个前导空白(leading blank)字符,字符"1"和 1 个尾部空白(trailing blank)字符组成,总共 20 个字符,调试器里的 Technical Type 显示为 C(20).

那为什么 strlen(lv_ss)不等于 20,而等于 19?SAP 帮助文档里给出了答案——SSTRING 即 CHAR20 这种变量,属于固定长度(fixed length)类型变量。当使用 strlen 函数计算这种变量的字符串个数时,尾部空白字符不应参加计数,所以要减一。

strlen( lv_s3) = 17
有了例三的基础,这个就很容易了。变量 lv_s3 类型是 CHAR18,属于固定长度类型变量,因此 strlen 计算出的字符串个数为 18 - 1 = 17.
第一个例子中,我们把一个整数直接赋给了一个字符串变量,发生了隐式类型转换。在实际项目中,这种隐式类型转换很容易出现在函数或者 ABAP 类方法的参数传递中。对于函数或 ABAP 类方法的形式参数,如果我们传递的实际参数类型和其类型不匹配,就会发生隐式类型转换,这种自动转换有时并非我们期望发生的,甚至容易被忽略。
看一个真实的例子。我曾经担任过一个俄罗斯的 SAP CRM 客户项目的 Dev Angel,收到过一个性能相关的 incident,客户打开某个 UI 的速度极其缓慢,甚至经常超时。
我通过调试,最终发现罪魁祸首位于下段代码。该代码从 SAP CRM 发起 RFC 调用,去 SAP ERP 读取数据,Max Hit 设置为 15,意思是期望 ERP 端至多返回 15 条记录。

然而从 ERP 端返回了总共 408093 条记录。显然,虽然通过硬编码指定 Max Hit 为 15,却完全没有起到限制作用。

起初我想当然地认为这是 ERP 函数的 bug,没有正确处理 CRM 调用端传递过来的 Max Hit. 然而当我在调试器里单步执行到 CRM 函数内部查看 iv_max_entries 时,一下傻了眼:

它的值从 15 一下变成了 3473457. 这个数字是什么鬼?!
再看函数的形式参数定义,iv_max_entries 类型为整型,而二次开发顾问传入的硬编码值'15', 是一个字符值,我顿时恍然大悟。

'15'是怎么变成魔幻数字 3473457 的?
Jerry 先不解释,而是请大家看下面这段代码:

执行,正好输出 3473457 这个魔幻数字。那么代码第四行 31003500 是哪里来的?其实就是字符串'15'的十六进制编码。

也就是说,二次开发顾问在 RFC 调用时,将硬编码的'15'传给了接受整型变量的函数参数 IV_MAX_ENTRIES. 应该该参数类型为整型,所以'15'的十六进制编码'31003500'被自动转换成了对应的整型数 3473457. 显然这不是开发顾问期望的行为,但因为程序能够继续运行,所以这个问题暂时被掩盖了。
而 RFC 调用完成之后,紧接着是一个嵌套的 LOOP. 在 Max Hit 能按照期望工作的前提下,对于最多包含 15 条记录的内表,就算进行嵌套的 LOOP 操作也能很快完成。但如今因为 Max Hit 不工作,内表记录从最多 15 条一下子变成了超过 40 万条,在这么庞大规模的内表上进行嵌套 LOOP 操作,性能可想而知。
经历过这次 incident 的处理之后,我个人觉得,使用隐式类型转换的最佳实践就是根本不去用它。程序员在工作的时候,必须时刻清醒地知道自己在做什么,要扼住编译器的咽喉,而不要被编译器扼住了咽喉。
SAP帮助文档上提到:当一个 buffered 数据库表被修改时,其 buffer 也会自动被更新,包括其他应用服务器实例上针对该表的 buffer 也会以异步的方式自动被更新。
Buffer 表被修改的记录会被写入一个中央日志表 DDLOG. Buffer 的异步更新就是应用服务器定期地查看该表是否存在新的记录而实现的。

我处于好奇,想在 SE16 里查看 DDLOG 的记录长什么样,发现 notebook 这个字段的类型为 LRAW:

为了做测试,我选择了一个 buffer 表,CRMC_PROC_TYPE, 往里面写入了一些数据:

然后 SE16,找不到我刚才插入的记录,记载在这张表的什么地方了:

我当然可以用 SELECT * 把包括 notebook 字段在内的所有数据都读取到 ABAP 内表里,但是我不知道如何解析类型为 LRAW 的字段,所以读出来也没用。
我国有句古话:毒蛇出没之处,七步之内必有解药。同样,DDLOG 这张表存储的数据,如果没有对应的读取 API,那么这些数据也毫无存在的价值。
所以对 DDLOG 执行 where used list 操作,必定可以找到读取该表的 API.

果然找到了一些有用的函数和类:

一些和这个 DDLOG 数据库表有关的操作和工具:
ABAP 数据库表 buffer 同步监控工具 RSDBBUF3

解析 DDLOG-NOTEBOOK 字段的函数:SBUF_SEL_DDLOG_RECS
用法如下:
使用这个函数,就能查到我之前修改数据库表 CRMC_PROC_TYPE 后自动写入表 DDLOG 的记录了。

感谢阅读。
版权声明: 本文为 InfoQ 作者【Jerry Wang】的原创文章。
原文链接:【http://xie.infoq.cn/article/acde54ae63534978d88574ea7】。文章转载请联系作者。
评论