首页 » ORACLE » 当C语言的程序处理 chr(0) or ‘\0’ 时的ORA-01008 Case

当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 —

打赏

,

对不起,这篇文章暂时关闭评论。