当C语言的程序处理 chr(0) or ‘\0’ 时的ORA-01008 Case
前几日遇到一套用C语言封装的数据复制程序故障, 同步的数据变化数据存储在数据库,但同步的流程控制是封装好的, 今天突然日志中出现了ORA-01008: not all variables bound, 该错误是语法级错误当SQL中的实际付值数量小于绑定变量数时提示. 不懂开发和网络的系统管理员不是好的DBA, 呵, 有时在我们处理数据库故障时如果对OS和对编程思想有经验的话, 可能对于性能优化或故障分析有一定的帮助.
程序的报错信息:
[1228181204 ] USQL:
update T_BSF_WEEJAR set
%s
where rowid=:ROW_ID
[1228181204 ] 运行过程出现异常:ORA-01008: not all variables bound
[1228181204 ] 异常记录:RowID=AAMTtcAAxAACRxdAAv, Table=T_UCP_ANBOB, SRowID=AAAHU2AFsAAFgmEAAd, Key=SUBWAYID=SS.HD.DA.ZY.005
对于上面的信息一头污水, 出现了两个表名, 无法获取具体出错的SQL文本. 从分析该问题首先是找到出错sql. 从数据库内部可以启用errorstack 1008 event捕捉出错时的SQL.
1, 对高级复制的ora会话启errorstack event
SQL> oradebug setospid <高级复制server processid> SQL> oradebug event 1008 trace name errorstack level 10;
2, 把错误记录加入同步队列, 出错后停止该事件.分析出错trace文件
----- Error Stack Dump -----
ORA-01008: not all variables bound
----- Current SQL Statement for this session (sql_id=5d2cw5ud331mm) -----
select
PARWAYID,
SUBWAYID,
HICHYOFFSET,
CREATETIME,
REGION,
SUBWAYNAME,
rowidtochar(ROWID)
from T_UCP_ANBOB
where
PARWAYID=:PARWAYID and <<<<<<<
SUBWAYID=:SUBWAYID
----- Bind Info (kxscoacd) -----
Bind#0
No oacdef for this bind. <<<<<<<
Bind#1
oacdty=01 mxl=32(15) mxlc=00 mal=00 scl=00 pre=00
oacflg=21 fl2=1000000 frm=01 csi=31 siz=0 off=0
No bind buffers allocated
Frames pfr 0x0000000000000000 siz=4656 efr 0x0000000000000000 siz=4640
Cursor frame dump
enxt: 3.0x00000268 enxt: 2.0x00000040 enxt: 1.0x00000f78
pnxt: 1.0x00000010
kxscphp=0x9ffffffffd320188 siz=984 inu=288 nps=224
Tip:
从trace中确认了报错时的SQL文本, 及确认了绑定变量的第一个参数没有值, 继续验证同步变化数据表中拼接该SQL变量的值是否是两部分,缺少绑定变量的值的个数才出现了上面的ORA-1008错误.
查看同步的数据,猜测是程序是把个同步表中多字段的值以指定的字符(本案例是char 3)分割拼接记录,在应用时再拆分. 查看出错的错误记录的字段值.开始使用的是TOAD, 奇怪是看到字段只有一部分(一个字段的值)”SUBWAYID=XXXX”, 继续查该值的生成器trigger, 其实就是一个DML trigger,然后批的字段名和值.trigger代码中拼接部分如下,(注意列的顺序)
key_str := 'SUBWAYID='||:old.SUBWAYID||','||chr(3)||
'PARWAYID='||:old.PARWAYID;
那为什么用TOAD看到的只有一部分? 用最权威的SQLPLUS查看, 却发现该记录而是和trigger拼接的一样有两部分组成, 为什么TOAD和复制程序都只能认识一部份呢?使用dump函数查看该字段值的编码.
Typ=1 Len=44: 83,85,66,87,65,89,73,68,61,72,66,46,72,68,46,68,65,46,90,89,46,48,48,53,0,44,3,80,65,82,87,65,89,73,68,61,72,66,46,72,68,46,68,65
注意在字段拼接中,准确说是在第一个字段部分的结尾有一个chr(0), 如果对C语言了解,”\0″ 字符是在C语言中标志字符串结束的终止符, 问题的原因就在这里. 因为字符串从数据库读出后在C语言中处理时被”\0″截断了,所以复制程序和TOAD显示只有”\0″前的一部分. update掉数据库中该字段的chr(0)的值, 问题得到解决. 当然彻底解决该问题要从程序才是最佳.
为了还原该问题写一段简单的C代码就可以演示了 \0的问题. “abc\0def” 后面的def被截断.:
[oracle@anbob ~]$ cat t.c #include int main() { char str[]="abc\0def"; printf("%s\n",str); return 0; } $ gcc t.c -o t $ ./t abc
好奇同样用JAVA代码做了测试,因为JAVA是面向对象的开发语句, 对象就有自己的固定大小, 所以在对字符处理中比C少了一位的\0 符也可以判断文本的结束,就像下面的代码中不会被char 0截断.
[oracle@anbob ~]$ vi t.java public class t{ // just test char zero by weejar(anbob.com) public static void main(String[] args){ char c=0; String v="string: anbob"+c+".com"+'\0'+" over"; System.out.println(v1); } } [oracle@anbob ~]$ /u01/app/oracle/product/12.2.0/db_1/jdk/bin/javac t.java [oracle@anbob ~]$ /u01/app/oracle/product/12.2.0/db_1/jdk/bin/java t string: anbob.com over
Summary:
多学无害, 对数据库中的值有些特殊的不显示ASCII码要注意,本案例就是因为写入一个CHAR 零在数据库中后期C语言时错误的处理, 如何从前端预防,还是后端判断处理,在开始设计时一定要周全.
— over —
对不起,这篇文章暂时关闭评论。