Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1693766
  • 博文数量: 136
  • 博客积分: 10021
  • 博客等级: 上将
  • 技术积分: 3261
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-22 11:26
文章分类

全部博文(136)

文章存档

2010年(1)

2009年(26)

2008年(109)

我的朋友

分类: Oracle

2008-07-31 13:04:44

这个 BUG 也是昨天碰到的。我的 Oracle 开发经验是在 Oracle9.2.0.1 平台获得的,昨天一段在 Oracle9.2.0.1 中正常执行的代码,在 Oracle10.2.0.1 却并没有按照我预想的流程执行。测试了一下,也可以归为 PL/SQL 编译器的 BUG。下面我再现一下这个问题:
 
1. 创建测试环境
 
在 SCHEMA 级创建两个 TYPE:一个只有属性(类似于 RECORD),另一个是基于它的嵌套表:
 
SQL> create type record_name as object ( id number(1), name varchar2(20) )
  2  /
 
类型已创建。
 
SQL> create type tnt_names is table of record_name
  2  /
 
类型已创建。
 
2. 正常的执行过程
 
使用 tnt_names 类型定义的变量 nt_names,使用前必须经过两次初始化,否则会出现错误。这一点在 Oracle9i 和 Oracle10g 中是一致的:
 
-- 两次初始化
SQL> set serveroutput on
SQL> declare
  2    nt_names tnt_names := tnt_names();
  3  begin
  4    nt_names.extend;
  5    nt_names(1) := record_name(null, null);
  6
  7    nt_names(1).id := 1;
  8    nt_names(1).name := 'yuechaotian';
  9
 10    dbms_output.put_line( 'id: ' || nt_names(1).id );
 11    dbms_output.put_line( 'name: ' || nt_names(1).name );
 12  end;
 13  /
id: 1
name: yuechaotian
 
PL/SQL 过程已成功完成。
-- 一次初始化
SQL> declare
  2    nt_names tnt_names := tnt_names();
  3  begin
  4    nt_names.extend;
  5    -- nt_names(1) := record_name(null, null);
  6
  7    nt_names(1).id := 1;
  8    nt_names(1).name := 'yuechaotian';
  9
 10    dbms_output.put_line( 'id: ' || nt_names(1).id );
 11    dbms_output.put_line( 'name: ' || nt_names(1).name );
 12  end;
 13  /
declare
*
ERROR 位于第 1 行:
ORA-06530: 引用未初始化的组合
ORA-06512: 在 line 8
 
注意看这里的错误提示:在第 8 行出现未初始化的引用错误,而不是第 7 行!这是为什么呢?
 
3. 异常的执行过程
 
对于出现错误的这段代码,我们将类型 record_name 中的属性全部换成 number 类型,再次测试。发现在 Oracle9i 和 Oracle10g 中执行结果是不同的:
 
SQL> select * from v$version;
 
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0      Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production
 
SQL> drop type tnt_names;
 
类型已丢弃。
 
SQL> create or replace type record_name as object (
  2  id number(1), name number(1) )
  3  /
 
类型已创建。
 
SQL> create type tnt_names is table of record_name
  2  /
 
类型已创建。
SQL> declare
  2    nt_names tnt_names := tnt_names();
  3  begin
  4    nt_names.extend;
  5    -- nt_names(1) := record_name(null, null);
  6
  7    nt_names(1).id := 1;
  8    nt_names(1).name := 2;
  9
 10    dbms_output.put_line( 'id: ' || nt_names(1).id );
 11    dbms_output.put_line( 'name: ' || nt_names(1).name );
 12  end;
 13  /
id: 1
name: 2
 
PL/SQL 过程已成功完成。
 
SQL> select * from v$version;
 
BANNER
-----------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
CORE    9.2.0.1.0       Production
TNS for 32-bit Windows: Version 9.2.0.1.0 - Production
NLSRTL Version 9.2.0.1.0 - Production
SQL> drop type tnt_names;
 
类型已丢弃。
 
SQL> create or replace type record_name as object (
  2  id number(1), name number(1) )
  2  /
 
类型已创建。
 
SQL> create type tnt_names is table of record_name
  2  /
 
类型已创建。
 
SQL> declare
  2    nt_names tnt_names := tnt_names();
  3  begin
  4    nt_names.extend;
  5    -- nt_names(1) := record_name(null, null);
  6
  7    nt_names(1).id := 1;
  8    nt_names(1).name := 2;
  9
 10    dbms_output.put_line( 'id: ' || nt_names(1).id );
 11    dbms_output.put_line( 'name: ' || nt_names(1).name );
 12  end;
 13  /
declare
*
ERROR 位于第 1 行:
ORA-06530: 引用未初始化的组合
ORA-06512: 在line 7
 
这就是昨天碰到的问题:在 Oracle10g 的一段代码中类似地使用了两个 TYPE,只进行了一次初始化,但执行时并没有报错(而这种情况在 Oracle9i 中是不会发生的)。
 
从我们刚才的测试看,它得到正确的结果,而且简化了编码,这怎么能说是 BUG 呢? 因为这样使用时,会有意想不到的影响:
 
4. 带来的影响
 
当使用 table( ) 函数封装嵌套表中的数据时,就出现问题了。我们继续在 Oracle10.2.0.1 中测试:
 
SQL> declare
  2    nt_names tnt_names := tnt_names();
  3    ref_cursor sys_refcursor;
  4
  5    n_id number(1);
  6    n_name number(2);
  7  begin
  8    nt_names.extend;
  9    -- nt_names(1) := record_name(null, null);
 10
 11    nt_names(1).id := 1;
 12    nt_names(1).name := 2;
 13
 14    dbms_output.put_line( 'id: ' || nt_names(1).id );
 15    dbms_output.put_line( 'name: ' || nt_names(1).name );
 16
 17    -- 将嵌套表中的结果使用 table( ) 函数封装
 18    open ref_cursor for
 19      select * from table(nt_names);
 20    fetch ref_cursor into n_id, n_name;
 21
 22    -- 输出游标中的结果:结果为 NULL
 23    dbms_output.put_line( 'n_id: ' || nvl( to_char(n_id), ' is null' ) );
 24    dbms_output.put_line( 'n_name: ' || nvl( to_char(n_id), ' is null' ) );
 25  end;
 26  /
id: 1
name: 2
n_id:  is null
n_name:  is null
 
PL/SQL 过程已成功完成。
 
在 18-20 行:使用 table() 函数封装嵌套表中的数据,并将结果赋值到两个变量;
在 23-24 行:输出这两个变量的值,发现其结果为 NULL。
 
跟昨天碰到的那个 Oracle816 中的 BUG 一样,这个 BUG 也不太容易触发:使用两个相互关联的 TYPE,第一个 TYPE 中的属性全部为 NUMBER 类型;只进行一次初始化;使用 table() 函数封装结果。
 
因为这个模块使用存储过程作为数据源生成 PB 中的数据窗口,所以得使用 table() 函数来为 PB 封装结果;并且使用自定义变量时,没有进行第二次的初始化;而恰好第一个 TYPE 中属性的类型全部为 NUMBER,所以触发到了这个 BUG。所带来的现象就是:编译通过的模块,无论怎么执行都没有数据,而且没有错误提示。
阅读(2385) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-10-29 16:05:05

en !