程序中出现奇怪错误“Ora-01001无效游标”

时间:2012-07-05 09:08:44

标签: oracle oracle11g

昨天我在生产过程中遇到了一个奇怪的错误。 声明执行失败

if v_cursor%isopen then
  close v_cursor; -- here was an error 
end if;

经过一番挖掘,我发现问题出现在打开这个光标的子程序中。我通过在子程序中添加输出参数sys_refcursor来修复bug。为了澄清情况,请考虑以下测试代码:

procedure nested_test(test  number,
                        p_cur out sys_refcursor)
  is  
    procedure nested_procedure_fail is
    begin      
      open p_cur for
        select 1, 2, 3, 4
          from dual
         where 1 = 0;
    end;

    procedure nested_procedure_success(p_cur out sys_refcursor) is
    begin
      open p_cur for
        select 1, 2, 3, 4
          from dual
         where 1 = 0;
    end;

  begin
    if test = 1 then
      nested_procedure_fail;
    else
      if test = 2 then
        nested_procedure_success(p_cur => p_cur);
      else
        open p_cur for
          select 6, 7, 8, 9
            from dual
           where 1 = 1;
      end if;
    end if;
  end;

  procedure test_fail is
    v_cur sys_refcursor;
  begin
    nested_test(test => 1, p_cur => v_cur);
    if v_cur%isopen then
      close v_cur;
    end if;
  end;

  procedure test_success is
    v_cur sys_refcursor;
  begin
    nested_test(test => 2, p_cur => v_cur);
    if v_cur%isopen then
      close v_cur;
    end if;
  end;

如果我尝试运行test_success一切正常,但在test_fail我收到了消息

  

ORA-01001:光标无效

我找不到任何有关此内容的信息。任何人都可以解释为什么这段代码失败了吗?

Oracle版本:

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0  Production
TNS for Solaris: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

2 个答案:

答案 0 :(得分:11)

这似乎是错误7174888,或者至少是与它密切相关的内容。这个描述是'当sys_refcursor传递给另一个过程时引发的ORA-6504',但如果我改变test_fail进行获取,我也可以做到这一点:

  procedure test_fail is
    v_cur sys_refcursor;
    a number;
    b number;
    c number;
    d number;
  begin
    nested_test(test => 1, p_cur => v_cur);
    if v_cur%isopen then
      fetch v_cur into a,b,c,d;
      close v_cur;
    end if;
  end;

我得到ORA-06504: PL/SQL: Return types of Result Set variables or query do not match

错误报告中的解决方法可以解决抓取和关闭问题。

  

将ref光标初始化为最高级别的非NULL值   它将被访问

  begin
    /* Dummy open to avoid bug 7174888 */
    open v_cur for 'select 1 from dual';
    nested_test(test => 1, p_cur => v_cur);
    if v_cur%isopen then
      fetch v_cur into a,b,c,d;
      close v_cur;
    end if;
  end;

答案 1 :(得分:2)

一个有趣的问题!只想添加一些东西。

对我而言,真正的问题在于依赖于IS_OPEN来确定游标是否有效。 Oracle可以出于多种原因抛出INVALID_CURSOR,并且可能有一个无效的“打开”游标。似乎有理由认为开放游标必须是有效的(因此我们可以从中获取或执行其他操作,如简单关闭),但情况不一定如此。

例如,您不能在远程过程调用中使用游标变量(通过dblinks)。同样的例子,即使使用Alex的解决方法,如果在1 db实例上调用open并且在另一个上执行fetch(如果nested_test,任何版本,在db_A上定义然后从db_B调用),则会失败。但是,ISOPEN的测试仍然会返回TRUE,但是然后尝试使用游标(fetch)会失败。

INVALID_CURSOR可以出于其他原因(例如超出最大打开游标,或者有时打开游标并等待一段时间再尝试使用它)。

所有这一切,我所知道的没有“ISVALID”测试。最好的方法是在同一个程序或子程序中打开,获取和关闭游标。创建一个负责打开游标的过程对我来说有点奇怪(但我确定有一些原因),并且可能导致难以解释的问题(比如这个)。如果你必须有另一个程序为你打开一个游标,那么你可能想要包含提取并最终在匿名块中关闭游标并捕获INVALID_CURSOR异常的代码。

只是我的随意; - )