[Database] 12c - Utl_Call_Stack...

原文はこちら。
http://tkyte.blogspot.jp/2013/06/12c-utlcallstack.html

来る数ヶ月にわたって、Oracle Database 12cの小さいけれどクールな新機能について執筆することになるでしょう。おそらくOracle.comのフロントページにはならないと思います。新しいパッケージ、UTL_CALL_STACKについて始めましょう。
Oracle® Database PL/SQL Packages and Types Reference 12c Release 1 (12.1)
UTL_CALL_STACK
http://docs.oracle.com/cd/E16655_01/appdev.121/e17602/u_call_stack.htm#ARPLS74078
以前、開発者は次の3個のファンクションにアクセスして「いったいコードのどこにいるんだ?」と実行中のコードの場所を把握していました。
さてこのルーティンは確かに有用なのですが、少々制限された利用でした。その理由を知るため、format_call_stackルーチンを見てみましょう。現在のコールスタックを出力するというだけのプロシージャを用意しました。
ops$tkyte%ORA12CR1> create or replace
  2  procedure Print_Call_Stack
  3  is
  4  begin
  5    DBMS_Output.Put_Line(DBMS_Utility.Format_Call_Stack());
  6  end;
  7  /
Procedure created.
さて、ネストされたファンクションと重複したファンクション名を持つパッケージがある場合…
ops$tkyte%ORA12CR1> create or replace
  2  package body Pkg is
  3    procedure p
  4    is
  5      procedure q
  6      is
  7        procedure r
  8        is
  9          procedure p is
 10          begin
 11            Print_Call_Stack();
 12            raise program_error;
 13          end p;
 14        begin
 15          p();
 16        end r;
 17      begin
 18        r();
 19      end q;
 20    begin
 21      q();
 22    end p;
 23  end Pkg;
 24  /

Package body created.
PKG.Pプロシージャを実行すると、結果は以下のようになるでしょう。
ops$tkyte%ORA12CR1> exec pkg.p
----- PL/SQL Call Stack -----
object line object
handle number name
0x6e891528 4 procedure OPS$TKYTE.PRINT_CALL_STACK
0x6ec4a7c0 10 package body OPS$TKYTE.PKG
0x6ec4a7c0 14 package body OPS$TKYTE.PKG
0x6ec4a7c0 17 package body OPS$TKYTE.PKG
0x6ec4a7c0 20 package body OPS$TKYTE.PKG
0x76439070 1 anonymous block

BEGIN pkg.p; END;


*
ERROR at line 1:
ORA-06501: PL/SQL: program error
ORA-06512: at "OPS$TKYTE.PKG", line 11
ORA-06512: at "OPS$TKYTE.PKG", line 14
ORA-06512: at "OPS$TKYTE.PKG", line 17
ORA-06512: at "OPS$TKYTE.PKG", line 20
ORA-06512: at line 1
赤の部分はformat_call_stackからの出力で、黒の部分はクライアントアプリケーションからのエラーメッセージです(format_error_backtraceというAPIを呼び出しても得られるでしょう)。おわかりのように、有用な情報が含まれていますが、利用するためには構文解析が必要で、思っているよりもトリッキーな可能性があります。これらの文字列の形式は固定ではなく、長年にわたって変化してきました(私は"who_am_i"、 "who_called_me"ファンクションを書きましたが、これらの文字列を構文解析することによって実現しました。信じて下さい、時間と共に変化するのです!)。

12cからは、コールスタックや一連のAPIコールへのアクセスが構造化され、この構造を問い合わせるようになりました。以下のようにprint_call_stackファンクションを書き換えてみましょう。
ops$tkyte%ORA12CR1> create or replace
 2  procedure Print_Call_Stack
  3  as
  4    Depth pls_integer := UTL_Call_Stack.Dynamic_Depth();
  5  
  6    procedure headers
  7    is
  8    begin
  9        dbms_output.put_line( 'Lexical   Depth   Line    Name' );
 10        dbms_output.put_line( 'Depth             Number      ' );
 11        dbms_output.put_line( '-------   -----   ----    ----' );
 12    end headers;
 13    procedure print
 14    is
 15    begin
 16        headers;
 17        for j in reverse 1..Depth loop
 18          DBMS_Output.Put_Line(
 19            rpad( utl_call_stack.lexical_depth(j), 10 ) ||
 20                    rpad( j, 7) ||
 21            rpad( To_Char(UTL_Call_Stack.Unit_Line(j), '99'), 9 ) ||
 22            UTL_Call_Stack.Concatenate_Subprogram
 23                       (UTL_Call_Stack.Subprogram(j)));
 24        end loop;
 25    end;
 26  begin
 27    print;
 28  end;
 29  /
ここで、コードのどの「位置(深さ)」にいるのか(utl_call_stack.dynamic_depth)がわかるので、ループを使ってスタックを上っていくことができます。lexical_depthと、実行していたユニット内の行番号に加えて、ユニット名も出力します。そして、任意のユニット名だけではなく、完全修飾されたユニット名で、パッケージ内のサブプログラム名を辿ることができます。しかし、それだけではなく、サブプログラム名の中のサブプログラム名の中のサブプログラム名も辿ることができます。例えば - 再びPKG.Pプロシージャを実行すると、その結果は以下のようになります。
ops$tkyte%ORA12CR1> exec pkg.p

Lexical Depth Line Name
Depth Number
------- ----- ---- ----
1 6 20 PKG.P
2 5 17 PKG.P.Q
3 4 14 PKG.P.Q.R
4 3 10 PKG.P.Q.R.P
0 2 26 PRINT_CALL_STACK
1 1 17 PRINT_CALL_STACK.PRINT


BEGIN pkg.p; END;

*
ERROR at line 1:
ORA-06501: PL/SQL: program error
ORA-06512: at "OPS$TKYTE.PKG", line 11
ORA-06512: at "OPS$TKYTE.PKG", line 14
ORA-06512: at "OPS$TKYTE.PKG", line 17
ORA-06512: at "OPS$TKYTE.PKG", line 20
ORA-06512: at line 1
今度は、以前format_call_stackを使って出力した行番号とパッケージ名よりも多くの情報を得ています。行番号とパッケージ(ユニット)名(サブプログラムの名前)を得るだけでなく、PがQを呼び、QがRを呼び、RがPを呼ぶというようなネストされたサブプログラムを把握することができます。また、我々は字句の深さを持つ、「本物の」呼び出しレベルを確認できること、パッケージからprint_call_stackにステップアウトし、順番に別のネストされたサブプログラムを呼び出していることが確認できることにご注目下さい。

この新しいパッケージはみなさんのエラーロギングパッケージへのすばらしい追加機能になるでしょう。もちろん、所有者名、コード実行時に効力のあるエディションなどを入手するためのその他のファンクションも含まれています。詳細情報はPL/SQL Packages and Types ReferenceのUTL_CALL_STACKの項をご覧下さい。
Oracle® Database PL/SQL Packages and Types Reference 12c Release 1 (12.1)
UTL_CALL_STACK
http://docs.oracle.com/cd/E16655_01/appdev.121/e17602/u_call_stack.htm#ARPLS74078

0 件のコメント:

コメントを投稿