7

昨日、私は生産手順の奇妙なバグに取り組みました。ステートメントで実行に失敗しました

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

少し掘り下げてみると、このカーソルを開いたサブプログラムに問題があることがわかりました。サブプログラムに出力パラメーターsys_refcursorを追加して、バグを修正しました。状況を明確にするために、次のテストコードを検討してください。

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
4

2 に答える 2

13

これはバグ 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

バグ レポートの回避策は、フェッチとクローズの問題の両方を解決します。

アクセスされる最上位レベルで参照カーソルを 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;
于 2012-07-05T11:12:57.573 に答える
2

興味深い質問です!いくつか追加したかっただけです。

私にとって、本当の問題は、カーソルが有効かどうかを判断するために IS_OPEN に依存することです。Oracle はさまざまな理由で INVALID_CURSOR をスローする可能性があり、無効な「オープン」カーソルを持つことができます。開いているカーソルが有効でなければならない (したがって、カーソルからフェッチしたり、単純なクローズなどの他の操作を実行したりできる) と仮定するのは合理的ですが、必ずしもそうとは限りません。

たとえば、(dblinks を介して) リモート プロシージャ コールでカーソル変数を使用することはできません。この同じ例は、Alex の回避策を使用しても、open が 1 つの db インスタンスで呼び出され、別のインスタンスで fetch が呼び出された場合に失敗します (任意のバージョンの nested_test が db_A で定義され、db_B から呼び出された場合)。ただし、ISOPEN のテストは引き続き TRUE を返しますが、カーソル (フェッチ) を使用しようとすると失敗します。

INVALID_CURSOR は、他の理由で発生する可能性があります (開いているカーソルの最大数を超えた場合や、カーソルを開いて使用する前にしばらく待機した場合など)。

そうは言っても、私が知っている「ISVALID」テストはありません。最善のアプローチは、同じプログラムまたはサブプログラム内でカーソルを開き、フェッチし、閉じることです。カーソルを開くだけの責任を持つプロシージャを作成することは、私には少し奇妙です (しかし、何らかの理由があると確信しています)。説明が難しい問題 (このようなもの) を引き起こす可能性があります。別のプログラムでカーソルをオープンする必要がある場合は、カーソルをフェッチして最終的にクローズするコードを無名ブロックで囲み、INVALID_CURSOR 例外をキャッチすることをお勧めします。

とりとめのない話です ;-)

于 2012-07-05T16:42:18.250 に答える